Navigation
- start by creating simple app with just one route
void main() => runApp(NavigationExampleApp());
class NavigationExampleApp extends StatelessWidget{
@override
Widget build(BuildContext context) => MaterialApp(
title: "Navigation Example",
home: FirstRoute(),
);
}
class FirstRoute extends StatelessWidget{
@override
Widget build(BuildContext context) => Scaffold(
body: Text("hello"),
);
}
- Use Navigator to move to the second page
RaisedButton(child: Text("to page 2"),onPressed: moveToSecondPage(context),)
final moveToSecondPage = (BuildContext ctx) =>
() =>
Navigator.push(ctx, MaterialPageRoute(builder:(ctx) => SecondRoute()));
- add return button on page 2
RaisedButton(
child: Text("back to A"),
onPressed: () => Navigator.pop(context),
)
Passing parameters
- pass parameters through constructor
//a parameter
class Message{
final String title;
final String content;
Message(this.title, this.content);
}
class SecondRoute extends StatelessWidget {
final Message _message;
const SecondRoute(this._message);
@override
Widget build(BuildContext context) => Scaffold(
body: Center(child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_message.title, style: TextStyle(fontWeight: FontWeight.bold),),
Text(_message.content),
(...)
RaisedButton(
child: Text("to page 2"),
onPressed: moveToSecondPage(context, Message("Message1", "Message1 Body")),
)
Testing Navigation
First of all it is convenient to add unique key to a widget we want to easily find in tests - in our case the button which navigates to second page
static const navigateToPage2Key = Key("gotoPage2");
RaisedButton(
key: navigateToPage2Key,
child: Text("to page 2"),
onPressed: moveToSecondPage(context, Message("Message1", "Message1 Body")),
)
Then we can write a widget test which is available thanks to flutter-test library
//https://api.flutter.dev/flutter/flutter_test/WidgetTester-class.html
testWidgets("navigation", (WidgetTester tester) async {
//We need to pump either whole application or
await tester.pumpWidget(NavigationExampleApp());
//this is why a key assigned to a widget is very convenient
final buttonToPage2=find.byKey(Key("gotoPage2"));
// "tap" actually represents pressing a button
final pressingButton=tester.tap(buttonToPage2);
//futures - explain this
await pressingButton;
//pumpAndSettle is important here so that whole new screen is loaded
await tester.pumpAndSettle(const Duration(milliseconds: 10));
expect(find.text("On Location 2"), findsOneWidget);
expect(find.text("Message1"), findsOneWidget);
});
test is just a simple async function and can be extract from invocation place to improve code redability
final WidgetTesterCallback navigateToPage3Test = (tester) async {
await tester.pumpWidget(NavigationExampleApp());
final buttonToPage2=find.byKey(Key("gotoPage3"));
await tester.tap(buttonToPage2);
await tester.pumpAndSettle();
expect(find.text("On Location 3"), findsOneWidget);
expect(find.text("Message2"), findsOneWidget);
};
testWidgets("navigation",navigateToPage3Test);
Named Routes
Widget build(BuildContext context) => MaterialApp(
title: "Navigation Example",
initialRoute: '/',
routes: {
'/' : (context) => FirstRoute(),
'/second' : (context) => SecondRoute(Message("TODO", "TODO")),
},
);
...
final moveToSecondPage =
(BuildContext ctx) => () => Navigator.pushNamed(ctx, '/second');
Named Routes - passing value
routes: {
'/': (context) => FirstRoute(),
'/second': (context) => SecondRoute(Message("TODO", "TODO")),
'/third': (context) => ThirdRoute(),
}
class ThirdRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Message message = ModalRoute.of(context).settings.arguments;
return (...)
.....................
final moveToThirdPage = (BuildContext ctx, Message m) => () => Navigator.pushNamed(
ctx,
'/third',
arguments: m,
);
Named Routes - receiving value
RaisedButton(
child: Text("YES"),
onPressed: (){
Navigator.pop(context,"YES");
},
),
RaisedButton(
child: Text("NO"),
onPressed: (){
Navigator.pop(context,"NO");
},
),
(...)
final OnPressContextBuilder resultFromFourthPage = (BuildContext ctx) => () async {
final result = await Navigator.pushNamed(
ctx,
'/fourth'
);
showDialog(context: ctx,builder: (ctx) => AlertDialog(title:Text("$result was choosen")));
};