when i use this method to resolve
setState() or markNeedsBuild() called during build problem
issue, SnackBar, EdgeAlert, Toast don't show on multiple click on FloatingActionButton but, after each Hot Reload, theme appear on screen,
_onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
for example:
body: Builder(
builder: (scaffoldContext) => Column(children: <Widget>[
Container(
child: Text('aaa'),
),
BindingWidget<_HomePageViewModel>(
bindings: <Binding>[
Binding('registerStatus', bindableBase,
_HomePageViewModel.registerStatusProperty,
bindingDirection: BindingDirection.TwoWay,
valueConverter: _NumberValueConverter())
],
builder: (bc) {
_onWidgetDidBuild(() {
var registerStatus =
BindingWidget.of<_HomePageViewModel>(bc)
.getValue('registerStatus') as String;
print('2) $registerStatus');
switch(int.parse(registerStatus)){
case 1:
EdgeAlert.show(context, title: 'Title', description: 'Description', gravity: EdgeAlert.TOP);
/*
Scaffold.of(scaffoldContext).showSnackBar(
SnackBar(
content: Text('dddddd'),
backgroundColor: Colors.red,
),
);*/
/* Toast */
break;
}
});
return Container();
}),
])),
how can i resolve this problem for this implementation my code?
thanks in advance
Related
I'm trying to create a menu that has a 'load more' functionality. From an interface perspective, PopupMenuButton has worked nicely, but I've been unable to dynamically refresh its content.
I'm using redux and I can successfully dispatch the action to fetch more, and the store is updated, but I don't see the change until I close the menu and re-open it, despite wrapping the PopupMenuButton in a StoreConnector. I also have a check for fetchInProgress that should be changing the bottom 'more' item to a spinner while the fetch is in progress, but that state change isn't noticed either.
I'm relatively new to Flutter so I'm wondering if I'm missing something.
Gif of the behavior
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, _ViewModel>(
converter: (store) => _ViewModel.fromStore(store, oneOnOneId),
builder: (ctx, vm) => PopupMenuButton(
onSelected: (callback) => callback(),
icon: Icon(Icons.expand_more),
itemBuilder: (_) =>
[...vm.pastOneOnOnes.map((m) {
return PopupMenuItem(
child: Center(child: Text(DateFormat('MM/dd/yyyy').format(m.meetingDate))),
value: () => {
Navigator.of(context).pushReplacementNamed(routeName,
arguments: {
'meetingId': m.id
})
}
);
}).toList(),
PopupMenuItem(
enabled: false,
child: Container(
height: 40,
width: double.infinity,
child: vm.fetchInProgress ?
Center(child: CircularProgressIndicator()) :
InkWell(
onTap: () => vm.fetchPastOneOnOnes(oneOnOneId, start: vm.pastOneOnOnes.length + 1),
child: Center(
child: Text('More', style: TextStyle(color: Colors.black))
)
),
),
value: null
)
]
),
);
}
}
You need to update the state when you make a change. When you call => vm.fetchPastOneOnOnes wrap it with setState :
onTap: () {
setState(){
vm.fetchPastOneOnOnes(...);}},
Im stuck in how I can manage to call a future Builder again on a button press to repeat the process indefinitely until it goes OK.
Here is my code:
Widget build(BuildContext context)
{
return FutureBuilder( future: sincroProcess(),
builder: (BuildContext context, AsyncSnapshot snapshot)
{
if (snapshot.hasData)
{
Map sincroProcessResponse = snapshot.data;
if( sincroProcessResponse['allok'] )
{
return ListBody(children: [ OutlinedButton
(
child: Text("Continue"),
onPressed: goInit(),
),
Text( sincroProcessResponse['msg'] )
]);
}
else
{
return ListBody(children: [ OutlinedButton
(
child: Text("Try again"),
onPressed: sincroProcess(), //need this to re-run
),
Text( sincroProcessResponse['msg'] )
]);
}
}
else
{
return ListBody(children: [ CircularProgressIndicator(backgroundColor: Colors.grey),
Text("We are preparing everything for your first usage =)...") ]);
}
});
}
}
I don't know if I am being clear enough, the behavior of the page should be:
show circular progress while running the sincroProcess, if the response its ok, show a button to go to another page, if not, show a button to re-run the sincroprocess with the circular progress indicator.
I cant imagine how to re-use my code!
Make your widget a Statefull widget and call setState() to rebuild the widget
...
else {
return ListBody(children: [
OutlinedButton(
child: Text("Try again"),
onPressed: (){setState((){});}, //need this to re-run // call setState
),
Text(sincroProcessResponse['msg'])
]);
}
writing an alertdialog inside a catch but if there is a finally it just ignores the alert dialog. the print just shows and escaping the dialog? and if I removed the finally it just works fine. help why this happening.
catch(error){
showDialog(context: context, builder: (ctx)=> AlertDialog(
title: Text("An ERROR accurred!"),
content: Text(error.toString()),
actions: [
FlatButton(onPressed: (){Navigator.of(ctx).pop();}, child: Text("OK"))
],
),
);
print(error.toString()+"from alert");
}
finally{
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
}
}
finally will be fired depending on any result.
How your code is working actually : First, catch is fired, showDialog is executed and dialog is shown, then print is executued. After these operation, finally section is exectued and Navigator.of(context).pop() pop (hide) your dialog. And since it is fast, you think your dialog is not shown.
The showDialog Widget also returns a Future so this means you need to use await until this code is executed before going to the finally block.
do it this way...
...
} catch (error) {
await showDialog<Null>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Something whent wrong'),
content: const Text('An Error ocurred'),
actions: [
FlatButton(
child: const Text('Okay'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
} finally { ....
I have an app comprising of home and update screens.
I am unable to navigate back to the home screen from the update screen.
See below code for home screen
// build the list widget
Widget _buildTaskWidget(task) {
return ListTile(
leading: Icon(Icons.assignment),
title: Text(task['name']),
subtitle: Text(task['created_at']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UpdateTask(task: task),
),
);
}
);
}
See below code for the update screen
#override
Widget build(BuildContext context) {
// final Task task = ModalRoute.of(context).settings.arguments;
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text('Update Task'),
),
body: ListView(
children: <Widget>[
inputWidget(),
inputWidgetForVendor(),
inputWidgetForAmount(),
Container(
margin: EdgeInsets.fromLTRB(45, 1, 45, 1),
child: RaisedButton(
color: Colors.blueAccent,
child: Text('Update Task', style: TextStyle(color: Colors.white)),
onPressed: () async {
var res = await updateNewTask(_taskTextInput.text, _vendorTextInput.text, _amountTextInput.text, id);
print(res);
Navigator.pop(context);
},
),
)
],
)// This trailing comma makes auto-formatting nicer for build methods.
);
}
If I remove the current onPressed function and replace with this below, it works
onPressed: () { Navigator.pop(context); },
What am I doing wrong in the initial function?
The update function successfully updates the list items, however I am unable to navigate back.
See below error logs:
E/flutter (27123): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>'
E/flutter (27123): #0 updateNewTask (package:task/repository/services.dart:67:10)
E/flutter (27123): <asynchronous suspension>
Please help.
Maybe outsourcing your async update function is a simple solution at this point, when you want to instantly go back to your home screen. You could print the update directly in the function then.
Just leave onpressed() as it is.
onPressed: () {
updateNewTask(_taskTextInput.text, _vendorTextInput.text, _amountTextInput.text, id);
Navigator.pop(context);
},
Yesterday I spent over ten hours trying to learn a bit of MobX and applying a simple SnackBar if there is an error coming from the API. My question is if the solution I found can be considered good and appropriate or there is a better one to be implemented.
class _LoginPageState extends State<LoginPage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _controller = Modular.get<LoginController>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
),
body: Observer(
builder: (context) {
if (_controller.token?.error != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(_controller.token?.error),
duration: Duration(seconds: 2),
));
});
}
return Center(
child: PrimaryButton(
onPressed: () => _controller.authenticate(),
text: 'Enviar',
icon: Icons.send,
),
);
},
),
);
}
}
In case you're curious about it, I'm using flutter_modular, hence the Modular.get<>()
I like this approach, that is as long as you make sure your snackbar does NOT cover the content of the page, as you know errors from API's could be complex and well documented, therefore you may come across a situation where the snackbar would cover your content.
I usually would use showDialog instead, as errors should not usually accur. when they do I would push a popup displaying and explaining the situation using the error details.
This is my customized version of popups:
class ButtonsAndAction extends FlatButton{
///Providing a func is "optional", just pass null if you want the defualt action to pop the navigator.
ButtonsAndAction(BuildContext context, String text, Function func ) : super(child: new Text(text, textDirection: Helper.textDirection(),style: TextStyle(color: ConstantValues.mainBackgroundColor)
,), onPressed: func == null ? () {Navigator.of(context).pop();} : func);
}
class Helper{
static TextDirection textDirection() => AppConfig.rtl ? TextDirection.rtl : TextDirection.ltr;
/// Used to push alerts of texts with a set of actions(buttons and actions) if wanted
static Future pushDialog(BuildContext context, String title, String body, {List<ButtonsAndAction> actions, bool dismissable = true}) {
return showDialog(
context: context,
builder: (BuildContext context) {
return new WillPopScope(
onWillPop: () async => dismissable,
child:
new AlertDialog(
shape: new RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(ConstantValues.roundRadius)),
side: BorderSide(color: ConstantValues.mainBackgroundColor, width: ConstantValues.roundBorderWidthForPopup)),
title: new Container(child: new Text(title, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor),), width: double.infinity,),
content: new Container(child: SingleChildScrollView(child:
new Text(body, textDirection: textDirection(), style: TextStyle(color: ConstantValues.mainBackgroundColor))),
width: double.infinity),
actions: actions
));
},
);
}
}
Good luck!