Flutter Flows made easy!
Usage
Define a Flow State
The flow state will be the state which drives the flow. Each time this state changes, a new navigation stack will be generated based on the new flow state.
class Profile {
const Profile ({this .name, this .age, this .weight});
final String ? name;
final int? age;
final int? weight;
Profile copyWith ({String ? name, int? age, int? weight}) {
return Profile (
name: name ?? this .name,
age: age ?? this .age,
weight: weight ?? this .weight,
);
}
}
Create a FlowBuilder
FlowBuilder is a widget which builds a navigation stack in response to changes in the flow state. onGeneratePages will be invoked for each state change and must return the new navigation stack as a list of pages.
FlowBuilder <Profile >(
state: const Profile (),
onGeneratePages: (profile, pages) {
return [
MaterialPage (child: NameForm ()),
if (profile.name != null ) MaterialPage (child: AgeForm ()),
];
},
);
Update the Flow State
The state of the flow can be updated via context.flow<T>().update.
class NameForm extends StatefulWidget {
@override
_NameFormState createState () => _NameFormState ();
}
class _NameFormState extends State <NameForm > {
var _name = '' ;
void _continuePressed () {
context.flow<Profile >().update ((profile) => profile.copyWith (name: _name));
}
@override
Widget build (BuildContext context) {
return Scaffold (
appBar: AppBar (title: const Text ('Name' )),
body: Center (
child: Column (
children: <Widget >[
TextField (
onChanged: (value) => setState (() => _name = value),
decoration: InputDecoration (
labelText: 'Name' ,
hintText: 'John Doe' ,
),
),
RaisedButton (
child: const Text ('Continue' ),
onPressed: _name.isNotEmpty ? _continuePressed : null ,
)
],
),
),
);
}
}
Complete the Flow
The flow can be completed via context.flow<T>().complete.
class AgeForm extends StatefulWidget {
@override
_AgeFormState createState () => _AgeFormState ();
}
class _AgeFormState extends State <AgeForm > {
int? _age;
void _continuePressed () {
context
.flow<Profile >()
.complete ((profile) => profile.copyWith (age: _age));
}
@override
Widget build (BuildContext context) {
return Scaffold (
appBar: AppBar (title: const Text ('Age' )),
body: Center (
child: Column (
children: <Widget >[
TextField (
onChanged: (value) => setState (() => _age = int .parse (value)),
decoration: InputDecoration (
labelText: 'Age' ,
hintText: '42' ,
),
keyboardType: TextInputType .number,
),
RaisedButton (
child: const Text ('Continue' ),
onPressed: _age != null ? _continuePressed : null ,
)
],
),
),
);
}
}
FlowController
A FlowBuilder can also be created with a custom FlowController in cases where the flow can be manipulated outside of the sub-tree.
class MyFlow extends StatefulWidget {
@override
State <MyFlow > createState () => _MyFlowState ();
}
class _MyFlowState extends State <MyFlow > {
late FlowController <Profile > _controller;
@override
void initState () {
super .initState ();
_controller = FlowController (const Profile ());
}
@override
Widget build (BuildContext context) {
return FlowBuilder (
controller: _controller,
onGeneratePages: ...,
);
}
@override dispose () {
_controller.dispose ();
super .dispose ();
}
}