Flutter Widget 101 Part 5 (Snackbars, Context, Builder and Key)

1. Aim

Through Snackbar, understand Context and how to use builder to access any context of a widget.

2.Snackbar

Code from Codelab
If you use Scaffold.of(context), there will be Context Error
"Scaffold.of() called with a context that does not contain a Scaffold"

import 'package:flutter/material.dart';


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

class SnackApp extends StatefulWidget {
  @override  _SnackAppState createState() => _SnackAppState();
}

class _SnackAppState extends State<SnackApp> {
  @override  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Snackbar exercise"),
      ),
      body: 
        new Center(
          child: new RaisedButton(

            onPressed: (){
              final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
              Scaffold.of(context).showSnackBar(snackBar);
            },
            child: new Text("Show Snackbar"),
          ),
        ),
    );
  }
}

This is because thecontext is of the parent widget that instantiated Scaffold. Not the contextof a child of Scaffold.Scaffold is not created yet. Solution 1. Use Builder. 3.Builder
What is a builder

A platonic (simplest possible thing of that kind) widget that calls a closure (lambda/anonymous function)  to obtain its child widget.

Widget and Context

In the Flutter framework, every widget has a build method that accepts a BuildContext parameter:
Widget build ( BuildContext context ) { ... }
This context object is passed to the widget's build function automatically by the framework. Hence there is no reason for any widget to have a constructor or function (aside from build) that would need to accept a context parameter.  
Thus you would not be able to pass a specific context object to a child.
You cannot call build() and pass your own context object manually without calling the build function twice (Your manual call and automatic call by the framework).
Thus you would not be able to pass a specific context object to a child.
You cannot call build() and pass your own context object manually without calling the build function twice (Your manual call and automatic call by the framework).
Purpose of Builder class
So, how can we pass a specific context object? 
Use Builder class to build and return child widgets.  This will pass a specific context object down to its children. The Builder class is basically your own build function that you setup. 
How?
Builder gives Child (Snackbar) access to/pass context of Parent (Scaffold) widget
Builder class constructor:
Builder({Key key, @required WidgetBuilder builder })
creates a widget by delegating its build to the callback function passed through its constructor. 
  1. Contains a BuildContext context parameter
  1. Builds and returns child widget(s) based on that context passed.
--------------------------------

Solution 1 (Passing Context via Builder)

import 'package:flutter/material.dart';


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

class SnackApp extends StatefulWidget {
  @override  _SnackAppState createState() => _SnackAppState();
}

class _SnackAppState extends State<SnackApp> {
  @override  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Snackbar exercise"),
      ),
      body: Builder(builder: (context){
        return new Center(
          child: new RaisedButton(

            onPressed: (){
              final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
              Scaffold.of(context).showSnackBar(snackBar);
            },
            child: new Text("Show Snackbar"),
          ),
        );
      }),
    );
  }
}

4. Key

What is a Key?
A Key is an identifier for Widgets, Elements (Instantiation of Widget) and SemanticsNode. 


A new widget will only be used to update an existing element if its key is the same as the key of the current widget associated with the element.


Usage of Key
Control matching of widgets by framework during rebuilds.  
Flutter matches widgets according to runtimeType, key and order of appearance.


Most useful in widgets that build many instances of the same type of widget. E.g. ShoppingList widget, builds many ShoppingListItem instances to fit screen.

Without key, Flutter would not know which element in list is removed.

Type of Keys

  • Key
  • GlobalKey
  • ValueKey
  • ObjectKey (e.g. of usage)
  • UniqueKey

Global Key
Expensive process

Allows element to be moved around (Unique global key in tree) without losing the state (Able to retrieve state).

Usage of Global Key
As Global Key is expensive.  Use it only when it matters e.g. Validation of Form

Solution 2 (Identifier through Global Key)


import 'package:flutter/material.dart';


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

class SnackApp extends StatefulWidget {
  @override  _SnackAppState createState() => _SnackAppState();
}

class _SnackAppState extends State<SnackApp> {

  final GlobalKey<ScaffoldState> _skey1 = GlobalKey<ScaffoldState>();

  void snackm(){
    _skey1.currentState.showSnackBar(SnackBar(content: Text('Yay! A SnackBar!')));
  }

  @override  Widget build(BuildContext context) {
    return new Scaffold(
      key: _skey1,
      appBar: new AppBar(
        title: new Text("Snackbar exercise"),
      ),
      body: Builder(builder: (context){
        return new Center(
          child: new RaisedButton(
            onPressed: (){
              //snackm();              _skey1.currentState.showSnackBar(SnackBar(content: Text('Yay! A SnackBar!')));
            },
            child: new Text("Show Snackbar"),
          ),
        );
      }),
    );
  }
}

Flutter Links:

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)

Flutter Widget 101 Part 4 (Navigation and Drawers)