Showing a snackbar as output from an action requires to create a sub context for the Scafold.of() as noted in the manual of Scaffold's of method.
But I can't find an example of this more "efficient method" described here.
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of.
I want to use this method since all that recursing indentation is as hard as it can be to read. I've already tried to create the submit button of my form with functions, and even tried to extend a RaisedButton class (so Scaffold.of
would be called inside a new instantiated Widget as noted in the docs) to no avail.
It only works if I use another Builder
inside the main Scaffold
of my app.
This works
class MyForm extends StatefulWidget {
Login({Key key, this.title}) : super(key: key);
final String title;
@override
_MyFormState createState() => new _MyFormState();
}
class _MyFormState extends State<MyForm> {
@override
Widget build(BuildContext context) {
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
return new Scaffold(
body: new Builder(
builder: (BuildContext context) {
return new ListView(
children: <Widget>[
myForm(context, _formKey),
],
);
},
),
);
}
}
class SubmitButton extends RaisedButton {
SubmitButton({
Key key,
this.onPressed,
this.child,
}) : super(key: key, onPressed: onPressed, child: child);
final VoidCallback onPressed;
final Widget child;
@override
Widget build(BuildContext context) {
return super.build(context);
}
}
Widget myForm(
BuildContext context,
GlobalKey<FormState> _formKey) => new Container(
child: new Form(
key: _formKey,
child: new Column(
children: <Widget>[
new TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Write Something';
}
},
),
new SubmitButton(
onPressed: () {
if (_formKey.currentState.validate()) {
Scaffold.of(context).showSnackBar(
new SnackBar(content: new Text('Processing'))
);
}
},
child: new Text('Submit'),
),
],
),
),
);
How do I remove the Builder
and simplify it?
I also tried to extend further RaisedButton
build()
method but got into a dependency / typing mess. And I can't find examples of this.
One simple way to split your build function into several widgets
is just to create the Scaffold in the build function of one Widget and the button in the build function of a different Widget. (Incidentally, the myForm
function in your code has no effect because it is run as part of the same build function, so the value of context is the same.) I refactored your code to introduce a MyPage Widget that builds the scaffold, and left the rest in the MyForm Widget. But, we now have two different build methods: one builds the Scaffold, and one the form and the button that needs to access the scaffold.
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new MyForm(),
);
}
}
class MyForm extends StatefulWidget {
@override
_MyFormState createState() => new _MyFormState();
}
class _MyFormState extends State<MyForm> {
final _formKey = new GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return new ListView(
children: <Widget>[
new Container(
child: new Form(
key: _formKey,
child: new Column(
children: <Widget>[
new TextFormField(
validator: (value) =>
(value.isEmpty) ? 'write something' : null,
),
new RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text('Processing'),
));
}
},
child: new Text('Submit'),
),
],
),
),
),
],
);
}
}