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
Related
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.
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,
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.
I want to click an item menu (PopupMenuItem) and go to another route using Navigator.push but context is undefined inside the method.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
final List<Choice> choices = const <Choice>[
const Choice(title: 'Settings', icon: Icons.settings),
const Choice(title: 'Log out', icon: Icons.exit_to_app),
];
#override
Widget build(BuildContext context) {
final title = 'MyTitle';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
PopupMenuButton<Choice>(
onSelected: onItemMenuPress,
itemBuilder: (BuildContext context) {
return choices.map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Row(
children: <Widget>[
Icon(
choice.icon,
),
Container(
width: 10.0,
),
Text(
choice.title,
),
],
));
}).toList();
},
),
],
),
body: Text("Hello world")
),
);
}
void onItemMenuPress(Choice choice) {
if (choice.title == 'Log out') {
print("Logout");
Navigator.push(context, MaterialPageRoute(builder: (context) => LogoutRoute()));
}
}
}
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
class LogoutRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Logout"),
),
body: Center(
child: Text("Screen"),
),
);
}
}
I have tried to pass a context in onItemMenuPress in this way:
void onItemMenuPress(Choice choice, BuildContext context)
but:
onSelected: onItemMenuPress(context)
is not working.
Neither this approach works:
onSelected: (Choice c) { Navigator.push(context, MaterialPageRoute(builder: (context) => LogoutRoute())); }
I was following this tutorial:
https://medium.com/flutter-community/building-a-chat-app-with-flutter-and-firebase-from-scratch-9eaa7f41782e
and there is a snippet of his code (similar to mine) that seems to work for him:
https://github.com/duytq94/flutter-chat-demo/blob/master/lib/main.dart
I refer to line 235 (onSelected) and lines 199-205 (actual onItemMenuPress method)
How is it possible? How can I salve?
Thanks
Here you have :
MyApp <------ context
--> MaterialApp
(--> Navigator built within MaterialApp)
--> Scaffold
--> App Bar
--> ...
So when you're using the context to find the Navigator, you're using a context for the MyApp which isn't under the navigator.
so we can either make a new Stateless or Stateful Widget subclass to contain your Scaffold, as the build function within those will point at that level instead, or we can use a Builder and define the builder callback (which has a context pointing at the Builder) to return the Scaffold.
Working Code we created new subclass - HomeScreen :
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'MyTitle';
return MaterialApp(
title: title,
home: HomeScreen(title),
);
}
}
class HomeScreen extends StatelessWidget {
final String title;
HomeScreen(this.title);
final List<Choice> choices = const <Choice>[
const Choice(title: 'Settings', icon: Icons.settings),
const Choice(title: 'Log out', icon: Icons.exit_to_app),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
PopupMenuButton<Choice>(
onSelected: (val) => onItemMenuPress(val, context),
itemBuilder: (BuildContext context) {
return choices.map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Row(
children: <Widget>[
Icon(
choice.icon,
),
Container(
width: 10.0,
),
Text(
choice.title,
),
],
));
}).toList();
},
),
],
),
body: Text("Hello world"));
}
void onItemMenuPress(Choice choice, BuildContext context) {
if (choice.title == 'Log out') {
print("Logout");
Navigator.push(
context, MaterialPageRoute(builder: (context) => LogoutRoute()));
}
}
}
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
class LogoutRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Logout"),
),
body: Center(
child: Text("Screen"),
),
);
}
}
It happens because your flutter SDK and dark SDK is not working properly. you can solve this issues upgrading your flutter sdk. go to the terminal and type
flutter upgrade --force
after that your fluter sdk and dark sdk will be upgrade, after the installation complete you will be fine.
I'm trying to create a Radio in a showDialog, however the animation that occurs on Radio does not appear in showDialog.
For example: when tapped in foo2 nothing happens, and when you exit in showDialog and go back to it, foo2 is selected.
Below is the code and a gif showing what is happening:
import "package:flutter/material.dart";
void main() {
runApp(new ControlleApp());
}
class ControlleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "My App",
home: new HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
enum _RadioGroup {
foo1,
foo2
}
class HomePageState extends State<HomePage> {
_RadioGroup _itemType = _RadioGroup.foo1;
void changeItemType(_RadioGroup type) {
setState(() {
_itemType = type;
});
}
void showDemoDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
);
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new InkWell(
onTap: (){
showDemoDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text("show"),
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo1,
onChanged: changeItemType
),
const Text("foo1"),
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo2,
onChanged: changeItemType
),
const Text("foo2"),
],
)
],
)
);
},
child: new Container(
margin: new EdgeInsets.only(top: 16.0, bottom: 8.0),
child: new Text("Show"),
),
)
],
),
)
);
}
}
Remember that components are immutable.
When you call showDialog, the content of that dialog won't change even if HomePage does.
The solution is easy. You need to refactor a bit your code to something like :
showDialog(
context: context,
builder: (context) => MyForm()
)
and instead of changing the state of HomePage, you instead change the state of MyForm.
example :
class Test extends StatelessWidget {
void onSubmit(String result) {
print(result);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () => showDialog(context: context, builder: (context) => MyForm(onSubmit: onSubmit)),
child: Text("dialog"),
),
),
);
}
}
typedef void MyFormCallback(String result);
class MyForm extends StatefulWidget {
final MyFormCallback onSubmit;
MyForm({this.onSubmit});
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
String value = "foo";
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("My form"),
children: <Widget>[
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "foo",
),
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "bar",
),
FlatButton(
onPressed: () {
Navigator.pop(context);
widget.onSubmit(value);
},
child: new Text("submit"),
)
],
);
}
}