Flutter Widget 101 Part 4 (Navigation and Drawers)

1. Drawer

Official Doc

  • Material Design has 2 mode of navigation, Tabs or/and Drawer.
  • Drawer slide in horizontally
    • Use ListView (Scrollable) or Column as Child
    • onTap Behaviour can be set
  • Wrapper: Material App > Scaffold > Drawer > ListView
Drawer: When tapped on row, will change Text in Main Page
void main(){
  runApp(new MaterialApp(home: new DrawerTemp()),);
}

class DrawerTemp extends StatefulWidget {
  @override  _DrawerTempState createState() => _DrawerTempState();
}

class _DrawerTempState extends State<DrawerTemp> {

  String TextValue = "Original";

  @override  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text("Drawer Test"),
        backgroundColor: Colors.lightBlueAccent,
      ),
      drawer: new Drawer(
        
        child: new ListView(
          children: <Widget>[
            
            new UserAccountsDrawerHeader(
                accountName: new Text ("User Name"),
                accountEmail: new Text("Mickey@gmail.com"),
                decoration: new BoxDecoration(color: Colors.red),
                currentAccountPicture: new CircleAvatar(child: new Icon(Icons.local_library)),
                otherAccountsPictures: <Widget>[
                  new CircleAvatar(child: new Icon(Icons.accessibility),),
                  new CircleAvatar(child: new Icon(Icons.account_circle),),
                ],
            ),
            
            new ListTile(
              trailing: new Icon(Icons.add_a_photo),
              title: new Text("At a Shopping Mall"),
              subtitle: new Text("Buying Food"),

              onTap: (){
              setState(() {TextValue = "I am lazy, Hello World";});
              Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
      body: new Center(child: new Text(TextValue),),
    );
  }
}
2.Navigation

Navigation.push(): add a route to navigator

Sample:
  Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => MyPage()));

returns Future



Navigation.pop(): return to previous page and remove route from navigator

bool pop <T extends Object>(BuildContext context, [T result])

Pop the top-most route off the navigator that most tightly encloses the given context.


Route: have to be created or use MaterialPageRoute with platform specific animation


main.dart
import 'package:flutter/material.dart';
import 'page1.dart';
import 'page2.dart';

void main(){
  runApp(new MaterialApp(home: new DrawerTemp()),);
}

class DrawerTemp extends StatefulWidget {
  @override  _DrawerTempState createState() => _DrawerTempState();
}

class _DrawerTempState extends State<DrawerTemp> {

  String TextValue = "Original";

  @override  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text("Drawer Test"),
        backgroundColor: Colors.lightBlueAccent,
      ),
      drawer: new Drawer(
        
        child: new ListView(
          children: <Widget>[
            
            new UserAccountsDrawerHeader(
                accountName: new Text ("User Name"),
                accountEmail: new Text("Mickey@gmail.com"),
                decoration: new BoxDecoration(color: Colors.red),
                currentAccountPicture: new CircleAvatar(child: new Icon(Icons.local_library)),
                otherAccountsPictures: <Widget>[
                  new CircleAvatar(child: new Icon(Icons.accessibility),),
                  new CircleAvatar(child: new Icon(Icons.account_circle),),
                ],
            ),
            
            new ListTile(
              trailing: new Icon(Icons.add_a_photo),
              title: new Text("At a Shopping Mall"),
              subtitle: new Text("Buying Food"),

              onTap: (){
              setState(() {TextValue = "I am lazy, Hello World";});
              Navigator.pop(context);
              },
            ),

            new ListTile(
              trailing: new Icon(Icons.arrow_forward_ios),
              title: new Text("Go Page 1"),
              subtitle: new Text("Shopping Cart"),

              onTap: (){
                Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context1)=>new page1("Page 1")));
              },
            ),

          new ListTile(
            trailing: new Icon(Icons.arrow_forward_ios),
            title: new Text("Go Page 2"),
            subtitle: new Text("Shopping Cart2"),

            onTap: (){
              Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context2)=>new page2("Page 2")));
            },
          ),

          ],
        ),
      ),
      body: new Center(child: new Text(TextValue),),
    );
  }
}

Page1
import 'package:flutter/material.dart';
import 'page2.dart';

class page1 extends StatelessWidget {

  final String mtext;
  page1(this.mtext);

  @override  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(title: new Text("Page 1"),backgroundColor: Colors.red,),
      body: new Center(
        child: new Column(
          children: <Widget>[
            new Text("Welcome to Page 1"),
            new RaisedButton(onPressed: ()
            {
              Navigator.pop(context);
            },
              child:new Text("Back to Home"),),

            new RaisedButton(onPressed: ()
            {
              Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context1)=>new page2("Page 2")));
            },
              child:new Text("To Shopping Cart 2"),),

          ],
        ),
      ),
    );
  }
}

Page2
import 'package:flutter/material.dart';
import 'page2.dart';

class page1 extends StatelessWidget {

  final String mtext;
  page1(this.mtext);

  @override  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(title: new Text("Page 1"),backgroundColor: Colors.red,),
      body: new Center(
        child: new Column(
          children: <Widget>[
            new Text("Welcome to Page 1"),
            new RaisedButton(onPressed: ()
            {
              Navigator.pop(context);
            },
              child:new Text("Back to Home"),),

            new RaisedButton(onPressed: ()
            {
              Navigator.of(context).push(new NoAnimationMaterialPageRoute(builder: (BuildContext context1)=>new page2("Page 2")));
            },
              child:new Text("To Shopping Cart 2"),),

          ],
        ),
      ),
    );
  }
}

class NoAnimationMaterialPageRoute<T> extends MaterialPageRoute<T> {
  NoAnimationMaterialPageRoute({
    @required WidgetBuilder builder,
    RouteSettings settings,
    bool maintainState = true,
    bool fullscreenDialog = false,
  }) : super(
      builder: builder,
      maintainState: maintainState,
      settings: settings,
      fullscreenDialog: fullscreenDialog);

  @override  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    return child;
  }
}



Modify animation (code from stack overflow)
  • If you want to disable material Override the MaterialPAgeRoute<T> Class and modify buildtransitions by returning nothing in the child.
BuildContext



Location of Widget in the tree.



The BuildContext for a particular widget can change location over time as the widget is moved around the tree. Because of this, values returned from the methods on this class should not be cached beyond the execution of a single synchronous function.






  @override
  Widget build(BuildContext context) {
    // here, Scaffold.of(context) returns null
    return Scaffold(
      appBar: AppBar(title: Text('Demo')),
      body: Builder(
        builder: (BuildContext context) {
          return FlatButton(
            child: Text('BUTTON'),
            onPressed: () {
              // here, Scaffold.of(context) returns the locally created Scaffold
              Scaffold.of(context).showSnackBar(SnackBar(
                content: Text('Hello.')
              ));
            }
          );
        }
      )
    );
  }
3. Navigation: Send new data to screen


  TodosScreen({Key key, @required this.todos}) : super(key: key);
  • It is a ,-separated list of expressions that can access constructor parameters and can assign to instance fields, even final instance fields.
  • This is handy to initialize final fields with calculated values.

Sending Data to Another Screen

Initialiser List

  DetailScreen({Key key, @required this.todo}) : super(key: key);
key parameter passed to the constructor is forwarded to the named parameter key of the unnamed constructor of the super class.


import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class Todo {
  final String title;
  final String description;

  Todo(this.title, this.description);
}

void main() {
  runApp(MaterialApp(
    title: 'Passing Data',
    home: TodosScreen(
      todos: List.generate(
        20,
            (i) => Todo(
          'Todo $i',
          'A description of what needs to be done for Todo $i',
        ),
      ),
    ),
  ));
}

class TodosScreen extends StatelessWidget {
  final List<Todo> todos;

  TodosScreen({Key key, @required this.todos}) : super(key: key);

  @override  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todos'),
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            onTap: () {
             //Navigator.push(context, MaterialPageRoute(builder: (context) => DetailScreen(dtodo: todos[index]),),);             Navigator.push(context, MaterialPageRoute(builder: (context)
                 {
                 return DetailScreen(dtodo: todos[index]);}
             ),//MaterialPageRoute             );//Navigation            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  // Declare a field that holds the Todo  final Todo dtodo;

  // In the constructor, require a Todo  DetailScreen({Key key, @required this.dtodo}) : super(key: key);

  @override  Widget build(BuildContext context) {
    // Use the Todo to create our UI    return Scaffold(
      appBar: AppBar(
        title: Text("${dtodo.title}"),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text('${dtodo.description}'),
      ),
    );
  }
}


Heroes Animation
To make your app more alive, add animation to the onClicked/Selected Widget e.g. Icon fly to the next page.

Tag - Identifier
Make sure both page have the same tag.

Hero(
  tag: "DeleteTag",
  child: Icon(
    Icons.delete,
    size: 50.0,
  ),
),

placeholderBuilder - Fill the empty space of the hero with a placeholder
placeholderBuilder: (context, widget) {
      return Container(
        height: 100.0,
        width: 100.0,
        child: CircularProgressIndicator(),
      );
    },
  ),

flightShuttleBuilder - further customise the animation and icon
  flightShuttleBuilder: (flightContext, animation, direction,
      fromContext, toContext) {
    return Icon(FontAwesomeIcons.rocket, size: 150.0,);
  },
),






Comments

Popular posts from this blog

Setting up Terminal in Android Studio for Flutter and Bash Profile (For Mac)

Flutter Widget 101 Part 1 (Basic of Flutters, Layouts, Rows and Columns)