Flutter: LateError (LateInitializationError: Field 'note' has not been initialized.) - database

I am working on a notes app where the added notes are stored in a SQLFlite DB. Opening the Notes detail page of the stored notes I get the following error:
Exception has occurred. LateError (LateInitializationError: Field
'note' has not been initialized.)
This is my code:
class NoteDtl extends StatefulWidget {
final int noteId;
const NoteDtl({
Key? key,
required this.noteId,
}) : super(key: key);
#override
_NoteDtlState createState() => _NoteDtlState();
}
class _NoteDtlState extends State<NoteDtl> {
late Note note;
bool isLoading = false;
#override
void initState() {
super.initState();
refreshNote();
}
Future refreshNote() async {
setState(() => isLoading = true);
this.note = await NotesDatabase.instance.readNote(widget.noteId);
setState(() => isLoading = false);
}
#override
Widget build(BuildContext context) => Scaffold(
backgroundColor: backGround,
appBar: AppBar(
backgroundColor: appbar,
actions: [editButton(), deleteButton()],
bottom: const TabBar(
isScrollable: true,
labelColor: Colors.orange,
indicatorSize: TabBarIndicatorSize.label,
indicatorColor: Colors.orange,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(text: "Page1"),
Tab(
text: "Page2",
)
])),
body: TabBarView(children: [
Page1(note: note),
Page2(
note: note,
),
]));
Widget editButton() => IconButton(
icon: Icon(Icons.edit_outlined),
onPressed: () async {
if (isLoading) return;
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AddEditNotePage(note: note),
));
refreshNote();
});
Widget deleteButton() => IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
await NotesDatabase.instance.delete(widget.noteId);
Navigator.of(context).pop();
},
);
}
class Page1 extends StatefulWidget {
const Page1({Key? key, required this.note}) : super(key: key);
final Note note;
#override
State<Page1> createState() => _Page1State();
}
class _Page1State extends State<Page1> {
#override
Widget build(BuildContext context) {
return Text(widget.note.description);
}
}
class Page2 extends StatefulWidget {
const Page2({Key? key, required this.note}) : super(key: key);
final Note note;
#override
State<Page2> createState() => _Page2State();
}
class _Page2State extends State<Page2> {
#override
Widget build(BuildContext context) {
return Text(widget.note.subdescription);
}
}
Does anyone knows how to fix this?

Your Note object is loaded asynchronously. The body will execute before that object gets loaded from the database. So you have to check that object availability there.
Declare that object like:
Note? note;
And change the build function as follows:
Widget build(BuildContext context) => note != null
? Scaffold(//Existing code)
: Scaffold(
body: Center(
child: const CircularProgressIndicator(),
));
It will show a progress indicator on screen till that note object is loaded. Instead of note != null check, you can also use that isLoading variable also.

Related

How to create a form using dynamic fields from a JSON array in Flutter

I want to create a form with dynamic fields. The fields are fetched from a database; these are determined by a user on the web application and then displayed to another user on the mobile application so the fields keep changing.
Although there are no errors in the code, the form does not display. I have simplified it to a single input field so as to test the logic. Below is what I have tried so far:
the file to create the form :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:mne/Network/api.dart';
import 'form_model.dart';
class CreateForm extends StatefulWidget {
const CreateForm({Key? key}) : super(key: key);
#override
State<CreateForm> createState() => _CreateFormState();
}
class _CreateFormState extends State<CreateForm> {
late List<Map<String, dynamic>> _values;
final List<FormFields> _formFields = <FormFields>[];
#override
void initState() {
super.initState();
_values = [];
}
Future<List<FormFields>> _fetchFormFields() async {
var res = await Network().getData('company/fields');
var fields = <FormFields>[];
if (res.StatusCode == 200) {
var body = json.decode(res.body);
var tdata = body['data'];
var formsJson = tdata;
for (var formJson in formsJson) {
fields.add(FormFields.fromJson(formJson));
}
}
return fields;
}
_displayForm(index) {
_fetchFormFields().then((value) {
_formFields.addAll(value);
});
if (_formFields[index].type == "text") {
return Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
decoration: InputDecoration(
labelText: _formFields[index].name,
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.green),
borderRadius: BorderRadius.circular(10)),
),
onChanged: (val) {},
));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: Column(
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemCount: _formFields.length,
itemBuilder: (context, index) {
return _displayForm(index);
}))
],
)));
}
}
the form model:
class FormFields {
String name;
String type;
String option;
FormFields({required this.name, required this.type, required this.option});
factory FormFields.fromJson(Map<String, dynamic> json) => FormFields(
name: json['name'],
type: json['type'],
option: json['option'],
);
Map<String, String> toJson() =>
{'name': name, 'type': type, 'option': option};
}
I want a circular progress indicator to show loading. I also want the user to move from the login directly to a display of the form so I put the create form class inside another one, to make it easier to reference in the login button. I called it test :
import 'package:flutter/material.dart';
import 'package:mne/Forms/create_form.dart';
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
bool _isLoading = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: [
if (_isLoading)
Container(
alignment: Alignment.center,
child: ElevatedButton(
child: const Text('Add Form'),
onPressed: () async {
setState(() {
_isLoading = false;
});
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CreateForm()));;
if (!mounted) return;
setState(() {
_isLoading = true;
});
},
))
else
const Center(
child: CircularProgressIndicator(backgroundColor: Color.fromRGBO(0, 161, 39, 1)),
)
]));
}
}
Currently, after clicking on the login button which navigates to the test page it displays the add form button.
On clicking the add form button, the circular progress indicator displays very briefly before displaying a blank page. Should I perhaps add all the possible field type options for it to work? Please let me know where I have gone wrong thanks

No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of()

Okay so my code work (I have some things to fix but I can run It )but I have this error, I think the problem is on the main because that's why the terminal say but I don't know how I can solve it. If somoene can help me with that I will be very greateful as always ty
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Firebase.initializeApp(),
builder: (context, snapshot) {
if(snapshot.hasError){
return Scaffold(
body: Center(
child: Text(snapshot.error.toString(
),),
),
);
}
if(snapshot.connectionState == ConnectionState.waiting){
return loading();
}
return MaterialApp(
themeMode: ThemeMode.system,
theme: myTheme.lightTheme,
darkTheme: myTheme.darkTheme,
home: SplashScreen(
),
}
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
// TODO: implement initState
super.initState();
Timer(Duration(seconds: 4), () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (_) => Todolist(
)));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'stuffandmore/panda.png',
height: 230,

Add data instead of overwriting it

When I use pop up AddPlanScreen to add a note to the widget, it overwrites the document instead of adding it. Registering, logging and setting data work properly.
What I've tried to do:
Using FirebaseFirestore.instance.runTransaction but I couldn't use + operator for String as I add data to map.
set(..., SetOptions(merge:true))
update method
Do I have to try to create a new Map and add data there? I am new to programming and will be very grateful for any suggestions.
This is a method I use to set and fetch data
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class MyProvider extends ChangeNotifier {
Map<String, dynamic> _names = {};
String name(String key) => _names[key];
void setName(String key, String newString) {
_names[key] = newString;
var firebaseUser = FirebaseAuth.instance.currentUser;
FirebaseFirestore.instance
.collection('Notes')
.doc(firebaseUser.uid)
.set(_names);
}
void fetchData() {
var firebaseUser = FirebaseAuth.instance.currentUser;
FirebaseFirestore.instance
.collection('Notes')
.doc(firebaseUser.uid)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
var data = documentSnapshot.data();
_names = data;
} else {
print('The document does not exist on the database');
}
});
}
}
This is Planner Screen where I show all the notes
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
import 'file:///C:/Users/krisk/AndroidStudioProjects/planner_app/lib/widgets/weekday_card.dart';
import 'package:provider/provider.dart';
class PlannerScreen extends StatefulWidget {
static const String id = 'planner_screen';
#override
_PlannerScreenState createState() => _PlannerScreenState();
}
class _PlannerScreenState extends State<PlannerScreen> {
Widget build(BuildContext context) {
Provider.of<MyProvider>(context, listen: false)
.fetchData();
var size = MediaQuery.of(context).size;
final double itemHeight = (size.height - 24) / 2;
final double itemWidth = size.width / 2;
return Scaffold(
backgroundColor: Color(0xFFcf9e9f),
body: Container(
child: GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: (itemWidth / itemHeight),
),
children: <Widget>[
WeekDayCard(
text: '',
),
WeekDayCard(text: 'Monday' ),
WeekDayCard(text: 'Tuesday'),
WeekDayCard(text: 'Wednesday'),
WeekDayCard(text: 'Thursday'),
WeekDayCard(text: 'Friday'),
WeekDayCard(text: 'Saturday'),
WeekDayCard(text: 'Sunday'),
WeekDayCard(text: 'Notes'),
],
),
),
);
}
}
This is associated WeekDayCard widget
import 'package:flutter/material.dart';
import 'package:my_planner_app/screens/addPlan_screen.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
class WeekDayCard extends StatefulWidget {
WeekDayCard({#required this.text, this.name});
final String name;
final String text;
#override
_WeekDayCardState createState() => _WeekDayCardState();
}
class _WeekDayCardState extends State<WeekDayCard> {
#override
Widget build(BuildContext context) {
return Consumer<MyProvider>(builder: (context, myProvider, child) {
return Card(
color: Color(0xFFFEEFCD),
elevation: 10,
child: Column(
children: [
Text(widget.text),
Text(Provider.of<MyProvider>(context).name(widget.text) ?? ''
),
Expanded(
child: InkWell(
onTap: () {
showModalBottomSheet(
backgroundColor: Color(0xFFFEEFCD),
context: context,
builder: (context) => AddPlanScreen(weekdayName: widget.text),
);
},
),
),
],
),
);
});
}
}
This is associated AddPlanScreen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/my_provider.dart';
class AddPlanScreen extends StatefulWidget {
final String weekdayName;
const AddPlanScreen({Key key, this.weekdayName}) : super(key: key);
#override
_AddPlanScreenState createState() => _AddPlanScreenState();
}
class _AddPlanScreenState extends State<AddPlanScreen> {
String name;
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: TextFormField(
onChanged: (text) {
name = text;
},
decoration: InputDecoration(
border: InputBorder.none,
),
minLines: 10,
maxLines: 30,
autocorrect: false,
),
),
FlatButton(
onPressed: () {
Provider.of<MyProvider>(context, listen: false)
.setName(widget.weekdayName, name);
Navigator.pop(context);
},
color: Colors.blue,
),
],
);
}
}
You should use set only when you are creating a document for the first time and you want to give it a designated ID (not randomly generated by firebase). Or, a second use is when you want to deliberately write over your existing data.
When you want to update a document, or a single value\entry in it, you just use: collection('yourCollection').doc('yourDocID').update({"nameOfnewField": "new data"})
This update method will not overwrite your existing document,it will only add a new field called "nameOfnewField", or if that field already exists, it will write over it only.
i.e if nameOfnewField was a value that was false, when you update it, with .update({"nameOfnewField": "true"}), it becomes true but the rest of the document is not changed.

How to fix cannot access video stream (NotAllowedError) in flutter webview to use html5+webRTC camera api?

I am using instascan.min js library to scan QR Codes in my web angular js application. but in my flutter web view, I got an error - Cannot access video stream (NotAllowedError), And I am unable to fix it. I don't have too much knowledge of flutter.
I have tried to give permission to camera access but it's not working.
//# My Flutter Code
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
void main () => runApp(MyApp());
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FOTOFACE WALLET',
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget{
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home>{
#override
Widget build(BuildContext context) {
return WebviewScaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(0),
child: AppBar(
automaticallyImplyLeading: true, // hides leading widget
backgroundColor: new Color(0xFF404E67),
)
),
url: "https://fotofacewallet.com",
initialChild: Center(
child: CircularProgressIndicator(),
),
);
}
}
//# this is my scanner code in angular js controller
$scope.scan = () => {
var overlay = $('.overlay'),
close = $('<div class="close" id="closescanbtn">close</div>');
overlay.append(close);
let scanner = new Instascan.Scanner({
video: document.getElementById('preview')
});
scanner.addListener('scan', function (content) {
scanner.stop();
$('.overlay').fadeOut();
$('.overlay').hide();
$scope.scanpayProcess(content);
});
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
if(cameras[1]){
scanner.start(cameras[1]);
} else {
scanner.start(cameras[0]);
}
} else {
alert('No cameras found.');
}
}).catch(function (e) {
alert(e);
});
$('.overlay').show();
}
I am expecting a camera view in a flutter web view.
I have done something like this
on my index.html page or the file that you have created in your angular side
I have added one button :
<button type="button" onclick="displayMsg()" class="btn btn-default btn-login">Scan</button>
and handled its click event as below and we will post message as 'scan'
<script type="text/javascript">
function displayMsg(){
Print.postMessage("scan");
}
</script>
added this package in my pubspec.yaml file
flutter_barcode_scanner: ^0.1.5+1
do run flutter pub get to update dependencies
then on my main.dart file
I have imported
import 'dart:async';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
in main.dart file added below code to handle the event triggered on displayMsg() function - 'scan' message
final Set<JavascriptChannel> jsChannels = [
JavascriptChannel(
name: 'Print',
onMessageReceived: (JavascriptMessage message) {
if(message.message == 'scan'){
print(message.message);
sBarcode(MyApp());
}
}),
].toSet();
sBarcode(someVal) async {
String bCode = await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true); print(bCode);
someVal.enterBarcode(bCode); // to get the scanned barcode
return; }
enterBarcode(barc) {
flutterWebViewPlugin.evalJavascript("document.getElementById('yourtextboxid').value="
+ barc); }
This is how my complete main.dart file looks now
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
const kAndroidUserAgent =
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
String selectedUrl = 'add_your_url_here';
// ignore: prefer_collection_literals
final Set<JavascriptChannel> jsChannels = [
JavascriptChannel(
name: 'Print',
onMessageReceived: (JavascriptMessage message) {
if(message.message == 'scan'){
//MyApp.startBarcode();
print(message.message);
sBarcode(MyApp());
}
}),
].toSet();
sBarcode(someVal) async {
String bCode = await FlutterBarcodeScanner.scanBarcode("#ff6666", "Cancel", true);
print(bCode);
someVal.enterBarcode(bCode);
return;
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final flutterWebViewPlugin = FlutterWebviewPlugin();
enterBarcode(barc) {
flutterWebViewPlugin.evalJavascript("document.getElementById('barcodenumber').value=" + barc);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
// '/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/': (_) {
return WebviewScaffold(
url: selectedUrl,
javascriptChannels: jsChannels,
withZoom: true,
withLocalStorage: true,
withJavascript: true,
hidden: true,
initialChild: Container(
color: Colors.white,
child: const Center(
child: Text('Loading...'),
),
),
);
},
},
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Instance of WebView plugin
final flutterWebViewPlugin = FlutterWebviewPlugin();
// On destroy stream
StreamSubscription _onDestroy;
// On urlChanged stream
StreamSubscription<String> _onUrlChanged;
// On urlChanged stream
StreamSubscription<WebViewStateChanged> _onStateChanged;
StreamSubscription<WebViewHttpError> _onHttpError;
StreamSubscription<double> _onProgressChanged;
StreamSubscription<double> _onScrollYChanged;
StreamSubscription<double> _onScrollXChanged;
final _urlCtrl = TextEditingController(text: selectedUrl);
final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent');
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _history = [];
#override
void initState() {
super.initState();
flutterWebViewPlugin.close();
_urlCtrl.addListener(() {
selectedUrl = _urlCtrl.text;
});
// Add a listener to on destroy WebView, so you can make came actions.
_onDestroy = flutterWebViewPlugin.onDestroy.listen((_) {
if (mounted) {
// Actions like show a info toast.
_scaffoldKey.currentState.showSnackBar(
const SnackBar(content: const Text('Webview Destroyed')));
}
});
// Add a listener to on url changed
_onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) {
if (mounted) {
setState(() {
_history.add('onUrlChanged: $url');
});
}
});
_onProgressChanged =
flutterWebViewPlugin.onProgressChanged.listen((double progress) {
if (mounted) {
setState(() {
_history.add('onProgressChanged: $progress');
});
}
});
_onScrollYChanged =
flutterWebViewPlugin.onScrollYChanged.listen((double y) {
if (mounted) {
setState(() {
_history.add('Scroll in Y Direction: $y');
});
}
});
_onScrollXChanged =
flutterWebViewPlugin.onScrollXChanged.listen((double x) {
if (mounted) {
setState(() {
_history.add('Scroll in X Direction: $x');
});
}
});
_onStateChanged =
flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) {
print(state.type);
if (mounted) {
setState(() {
_history.add('onStateChanged: ${state.type} ${state.url}');
});
}
});
_onHttpError =
flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) {
if (mounted) {
setState(() {
_history.add('onHttpError: ${error.code} ${error.url}');
});
}
});
}
#override
void dispose() {
// Every listener should be canceled, the same should be done with this stream.
_onDestroy.cancel();
_onUrlChanged.cancel();
_onStateChanged.cancel();
_onHttpError.cancel();
_onProgressChanged.cancel();
_onScrollXChanged.cancel();
_onScrollYChanged.cancel();
flutterWebViewPlugin.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _urlCtrl),
),
RaisedButton(
onPressed: () {
flutterWebViewPlugin.launch(
selectedUrl,
rect: Rect.fromLTWH(
0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
userAgent: kAndroidUserAgent,
invalidUrlRegex:
r'^(https).+(twitter)', // prevent redirecting to twitter when user click on its icon in flutter website
);
},
child: const Text('Open Webview (rect)'),
),
RaisedButton(
onPressed: () {
flutterWebViewPlugin.launch(selectedUrl, hidden: true);
},
child: const Text('Open "hidden" Webview'),
),
RaisedButton(
onPressed: () {
flutterWebViewPlugin.launch(selectedUrl);
},
child: const Text('Open Fullscreen Webview'),
),
RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed('/widget');
},
child: const Text('Open widget webview'),
),
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _codeCtrl),
),
RaisedButton(
onPressed: () {
final future =
flutterWebViewPlugin.evalJavascript(_codeCtrl.text);
future.then((String result) {
setState(() {
_history.add('eval: $result');
});
});
},
child: const Text('Eval some javascript'),
),
RaisedButton(
onPressed: () {
setState(() {
_history.clear();
});
flutterWebViewPlugin.close();
},
child: const Text('Close'),
),
RaisedButton(
onPressed: () {
flutterWebViewPlugin.getCookies().then((m) {
setState(() {
_history.add('cookies: $m');
});
});
},
child: const Text('Cookies'),
),
Text(_history.join('\n'))
],
),
),
);
}
}
check and test and let me know if that works for you or not, This is working fine at my end.
Print will generate error in browser you need to test this in android or ios.
Hope this helps.
You get the Cannot access video stream (NotAllowedError) error because you need to grant the right permissions to the webview.
For Android, in the AndroidManifest.xml, you need to add these permissions:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
But this is not enough! To request permissions about the camera and microphone, you can use the permission_handler plugin.
So, for the webview, you can use my plugin flutter_inappwebview and use the androidOnPermissionRequest event for Android, that is an event fired when the WebView is requesting permission to access the specified resources (that is the Android native WebChromeClient.onPermissionRequest event).
An example of using WebRTC that works on Android:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Permission.camera.request();
await Permission.microphone.request();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: InAppWebViewPage()
);
}
}
class InAppWebViewPage extends StatefulWidget {
#override
_InAppWebViewPageState createState() => new _InAppWebViewPageState();
}
class _InAppWebViewPageState extends State<InAppWebViewPage> {
InAppWebViewController _webViewController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InAppWebView")
),
body: Container(
child: Column(children: <Widget>[
Expanded(
child: Container(
child: InAppWebView(
initialUrl: "https://appr.tc/r/158489234",
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
mediaPlaybackRequiresUserGesture: false,
debuggingEnabled: true,
),
),
onWebViewCreated: (InAppWebViewController controller) {
_webViewController = controller;
},
androidOnPermissionRequest: (InAppWebViewController controller, String origin, List<String> resources) async {
return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
}
),
),
),
]))
);
}
}
This example uses the room 158489234 on https://appr.tc/, that is a video chat demo app based on WebRTC (https://github.com/webrtc/apprtc).
To get it work, you need to set the option mediaPlaybackRequiresUserGesture to false and implement (for Android) the onPermissionRequest event.

Flutter - Blinking button

I need a call the user attention to a button. The first idea that came to mind is to add a blink animation. I really don't know how to do that, but I tried to make it work with the following code:
Timer timer = new Timer(new Duration(seconds: 1), () {
//basic logic to change the color variable here
setState(() {});
});
It is straightforward, every second setState is called and the widget is created again.
But it doesn't work, the timer is called only once. And, besides that, calling setState within a Timer seems wrong to me.
There is a better approach to this?
You can achieve this in an easy way using AnimationController and FadeTransition widget, here you have the code:
class MyBlinkingButton extends StatefulWidget {
#override
_MyBlinkingButtonState createState() => _MyBlinkingButtonState();
}
class _MyBlinkingButtonState extends State<MyBlinkingButton>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
#override
void initState() {
_animationController =
new AnimationController(vsync: this, duration: Duration(seconds: 1));
_animationController.repeat(reverse: true);
super.initState();
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationController,
child: MaterialButton(
onPressed: () => null,
child: Text("Text button"),
color: Colors.green,
),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
Usage:
main() {
runApp(
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Material(
child: Center(
child: MyBlinkingButton(),
),
),
),
);
}
DartPad example
Result:
You can do that with this approach also.
My logic is a little different I am using alternate for animation. Once animation completed in forward I'm coming backward.
Which is good for eyesight
ie:
forward -> backward
backward -> forward
and so on
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Wordpress App',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new BlinkAnimation(),
);
}
}
class BlinkAnimation extends StatefulWidget {
#override
_BlinkAnimationState createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
Animation<Color> animation;
AnimationController controller;
initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.linear);
animation =
ColorTween(begin: Colors.white, end: Colors.blue).animate(curve);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text('Blink Animation'),
),
body: new Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Container(
child: new RaisedButton(
color: animation.value,
onPressed: () {
controller.forward();
},
child: Text('Blink Animation'),
),
);
},
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
Here is the example from the answer by #nitishk72 but with code updated to null safety and more recent Flutter version.
Result:
Code:
You can just copy this to DartPad.dev and it will work :)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Blink animation demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: BlinkAnimation(),
);
}
}
class BlinkAnimation extends StatefulWidget {
#override
_BlinkAnimationState createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
late Animation<Color?> animation;
late AnimationController controller;
#override
initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.linear);
animation =
ColorTween(begin: Colors.white, end: Colors.blue).animate(curve);
// Keep the animation going forever once it is started
animation.addStatusListener((status) {
// Reverse the animation after it has been completed
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
setState(() {});
});
// Remove this line if you want to start the animation later
controller.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Blink Animation'),
),
body: Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) {
return Container(
color: animation.value,
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
// Start the animation or do something else on click
// controller.forward();
print('button does something!');
},
child: const Text('Blink Animation'),
),
);
},
),
),
);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
}
Current answers are great but do not cover opacity blink (repeated fade in, fade out), you may use the following widget if that's your goal:
class BlinkAnimation extends StatefulWidget {
final Widget child;
const BlinkAnimation({
Key? key,
required this.child,
}) : super(key: key);
#override
State<BlinkAnimation> createState() => _BlinkAnimationState();
}
class _BlinkAnimationState extends State<BlinkAnimation>
with SingleTickerProviderStateMixin {
late final AnimationController controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
late final Animation<double> animation = CurvedAnimation(
parent: controller,
curve: Curves.easeIn,
);
#override
Widget build(BuildContext context) {
return FadeTransition(opacity: animation, child: widget.child);
}
#override
dispose() {
controller.dispose();
super.dispose();
}
}
based on FadeTransition

Resources