• Elementary foundation series

A ListView of Flutter development

Detailed description of the layout of Flutter development

  • Project Actual Combat series

Flutter development practice high emulation wechat (1) home page

Flutter development combat high imitation wechat (2) found page

Flutter development practice high imitation wechat (a) home page

Source address: flutter_wetchat

1. Develop a HomePage page

  1. Operation effect:
  2. Function is introduced
  3. The code on
  • KYLRootPage is the root page
class KYLRootPage extends StatefulWidget {@override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return_RootPageState(); }}class _RootPageState extends State<KYLRootPage> {
  int _currentIndex = 0;

  List<Widget> pages = [Scaffold(
    appBar: AppBar(
      title: Text(' wechat '),), body:Center(
      child: Text(' wechat homepage '),,),Scaffold(
      appBar: AppBar(
        title: Text(' address book '),), body:Center(
        child: Text(' address book list '),),),Scaffold(
      appBar: AppBar(
        title: Text(' find '),), body:Center(
        child: Text(' discovery list '),),),Scaffold(
      appBar: AppBar(
        title: Text(' I '),), body:Center(
        child: Text(' my page '),),)]; @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      child: Scaffold(
      bottomNavigationBar: BottomNavigationBar(
        onTap: (int index) {
          _currentIndex = index;
        },
          type: BottomNavigationBarType.fixed,
          fixedColor: Colors.green,
          currentIndex: _currentIndex,
          items: <BottomNavigationBarItem> [BottomNavigationBarItem(
          icon: Icon(Icons.chat),
          title: Text(' wechat '),,BottomNavigationBarItem(
          icon: Icon(Icons.bookmark),
          title: Text(' address book '),),BottomNavigationBarItem(
          icon: Icon(Icons.history),
          title: Text(' find '),,BottomNavigationBarItem(
          icon: Icon(Icons.person_outline),
          title: Text(' id '),),]), body: pages[_currentIndex],); }}Copy the code
  • main.dart
import 'package:flutter/material.dart';
import 'KYLRootPage.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: KYLRootPage()); }}Copy the code

2. Explain the knowledge points used

2.1 BottomNavigationBar

This is a custom Button that you can place on top of the BottomNavigationBar, which implements Material(Android) and Cupertino(iOS) styles.

Scaffold is the scaffolding for the Root Widget-MaterialApp. It encapsulates AppBar, Drawer, SnackBar, BottomNavigationBar and so on that Material Design App will use. The BottomNavigationBarType has fixed and shifting styles, which can be distinguished only if there are more than three. Generally, we use fixed type to experience consistency.

The BottomNavigationBar is a StatefulWidget that can be analyzed using the following steps: 1. Look at the state it holds; 2. 2. look at his life cycle realization; 3. Carefully analyze its build method.

  • Holding state
List<AnimationController> _controllers = <AnimationController> [];List<CurvedAnimation> _animations;

// A queue of color splashes currently being animated.
final Queue<_Circle> _circles = Queue<_Circle>();

// Last splash circle's color, and the final color of the control after
// animation is complete.
Color _backgroundColor;
Copy the code

The first three properties are all related to animation, and the fourth is setting the background. Why does the BottomNavigationBar have no variable to mark which item is currently selected?

One of the rules of functional programming is to keep functions as pure as possible. CurrentIndex is a property that relies on being passed in from the outside and refires Render every time it changes. If you maintain it yourself, you also need to provide a callback method for external calls that returns the latest currentIndex value.

  • Lifecycle approach
ResetState initializes the above state attributes
@override
//initState has a subtle operation: _controllers[widget.currentIndex]. Value = 1.0;
void initState() {
  super.initState();
  _resetState();
}

// Recycle the resource
@override
void dispose() {
    for (AnimationController controller in _controllers)
      controller.dispose();
    for (_Circle circle in _circles)
      circle.dispose();
    super.dispose();
  }

// The Flutter system calls back to this method when properties change. Reinitialize directly when the number of items changes; When index changes, animate accordingly.
@override
void didUpdateWidget(BottomNavigationBar oldWidget) {
    super.didUpdateWidget(oldWidget);

    // No animated segue if the length of the items list changes.
    if(widget.items.length ! = oldWidget.items.length) { _resetState();return;
    }

    if(widget.currentIndex ! = oldWidget.currentIndex) {switch (widget.type) {
        case BottomNavigationBarType.fixed:
          break;
        case BottomNavigationBarType.shifting:
          _pushCircle(widget.currentIndex);
          break;
      }
      _controllers[oldWidget.currentIndex].reverse(a); _controllers[widget.currentIndex].forward(); }if(_backgroundColor ! = widget.items[widget.currentIndex].backgroundColor) _backgroundColor = widget.items[widget.currentIndex].backgroundColor; }//
@override
Widget build(BuildContext context) {}
Copy the code
  • Analysis build method
@override
  Widget build(BuildContext context) {
    / / check the debug
    assert(debugCheckHasDirectionality(context));
    assert(debugCheckHasMaterialLocalizations(context));

    // Labels apply up to _bottomMargin padding. Remainder is media padding.
    final double additionalBottomPadding = math.max(MediaQuery.of(context).padding.bottom - _kBottomMargin, 0.0);
    
    // Set the background color according to BottomNavigationBarType
    Color backgroundColor;
    switch (widget.type) {
      case BottomNavigationBarType.fixed:
        break;
      case BottomNavigationBarType.shifting:
        backgroundColor = _backgroundColor;
        break;
    }
    return Semantics( // Semantics is used to implement accessibility
      container: true,
      explicitChildNodes: true,
      child: Stack(
        children: <Widget> [Positioned.fill(
            child: Material( // Casts shadow.
              elevation: 8.0,
              color: backgroundColor,
            ),
          ),
          ConstrainedBox(
            constraints: BoxConstraints(minHeight: kBottomNavigationBarHeight + additionalBottomPadding),
            child: Stack(
              children: <Widget> [Positioned.fill(  // Click on the circular type of ripple animation
                  child: CustomPaint(
                    painter: _RadialPainter(
                      circles: _circles.toList(),
                      textDirection: Directionality.of(context),
                    ),
                  ),
                ),
                Material( // Splashes.
                  type: MaterialType.transparency,
                  child: Padding(
                    padding: EdgeInsets.only(bottom: additionalBottomPadding),
                    child: MediaQuery.removePadding(
                      context: context,
                      removeBottom: true.// tiles is just the bottom navigationtile, where you put the BottomNavigationBarItem
                      child: _createContainer(_createTiles()),
                    )))]))]));
  }}
Copy the code
  • _BottomNavigationTile see
Widget _buildIcon() {
    ...
    / / build Icon
  }

  Widget _buildFixedLabel() {
   ....
          // The matrix is used to animate the text, smoother
          // The font size should grow here when active, but because of the way
          // font rendering works, it doesn't grow smoothly if we just animate
          // the font size, so we use a transform instead.
          child: Transform(
            transform: Matrix4.diagonal3(
              Vector3.all(
                Tween<double>(
                  begin: _kInactiveFontSize / _kActiveFontSize,
                  end: 1.0,
                ).evaluate(animation),
              ),
            ),
            alignment: Alignment.bottomCenter,
            child: item.title,
          ),
        ),
      ),
    );
  }

  Widget _buildShiftingLabel() {
    return Align(...// The shift label is a fade animation, and only the currently selected label is displayed
        child: FadeTransition(
          alwaysIncludeSemantics: true,
          opacity: animation,
          child: DefaultTextStyle.merge(
            style: const TextStyle(
              fontSize: _kActiveFontSize,
              color: Colors.white,
            ),
            child: item.title,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    int size;
    Widget label;
    // Generate different labels
    switch (type) {
      case BottomNavigationBarType.fixed:
        size = 1;
        label = _buildFixedLabel();
        break;
      case BottomNavigationBarType.shifting:
        size = (flex * 1000.0).round();
        label = _buildShiftingLabel();
        break;
    }
    return Expanded(... children: <Widget>[
                  _buildIcon(),
                  label,
                ],
              ),
            ),
            Semantics(
              label: indexLabel,
}
Copy the code

2.2 the Container

Introduction of 2.2.1.

Containers are all too common in Flutter. The official description is a widget that combines painting, positioning, and sizing widgets. It is a composite widget with drawing widgets, positioning widgets, and sizing widgets inside. Many of the widgets you’ll see later are made up of more basic widgets.

An 2.2.2.
  1. The components of a Container are as follows:

The innermost layer is the child element; The child element is first wrapped in the padding; Then add additional constraints to the constraints; Finally add margin.

  1. The process for drawing containers is as follows:

The transform effect is drawn first; Next, draw decoration; Then draw child; Finally, draw the ground decoration.

  1. The Container can be adjusted in two ways:

The Container tries to be large enough when it has no children. Unless constraints are unbounded, in which case the Container will try to be small enough. The Container of the string node resizesaccording to the size of its children, but the Container constructor resizesaccording to the parameters in the constructor if the Container constructor contains width, height, and constraints.

2.2.3. Attributes of Containers
  • Key: Unique identifier of Container, used to search for updates.

  • Alignment: Controls the alignment of the child. This attribute takes effect if the Container or its parent is larger than the child size. There are many alignment options.

  • Padding: The empty area inside the decoration. If there is a child, the child is inside the padding. The difference between padding and margin is that padding is contained within the content, while margin is the outer boundary. If you set the click event, the padding area will respond, but the margin area will not.

  • Color: Used to set the background color of the container. If the foregroundDecoration is set, the color effect may be obscured.

  • “Decoration” : the decoration behind the child. If “decoration” is set, the color attribute cannot be set. Otherwise, an error will be reported, and the color should be set in “decoration”.

  • ForegroundDecoration: Decoration painted in front of child.

  • Width: the width of the container, set to double. Infinity can be used to force the container to be full in width.

  • Height: The height of the container, set to double. Infinity forces the container to be full at the height.

  • Constraints: Additional constraints added to the child.

  • Margin: The blank area surrounding decoration and child, not part of the content area.

  • Transform: Sets the transformation matrix of container. The type is Matrix4.

  • Child: Content widget in container.

Example:

new Container(
  constraints: new BoxConstraints.expand(
    height:Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
  ),
  decoration: new BoxDecoration(
    border: new Border.all(width: 2.0, color: Colors.red),
    color: Colors.grey,
    borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
    image: new DecorationImage(
      image: new NetworkImage('http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f 06c.jpg'),
      centerSlice: new Rect.fromLTRB(270.0.180.0.1360.0.730.0),
    ),
  ),
  padding: const EdgeInsets.all(8.0),
  alignment: Alignment.center,
  child: new Text('Hello World',
    style: Theme.of(context).textTheme.display1.copyWith(color: Colors.black)),
  transform: new Matrix4.rotationZ(0.3),Copy the code
2.2.4. Use the Container

Container is one of the most frequently used widgets in the current project. In practice, I use Containers in the following situations, but not always, but also through other widgets.

  1. You need to set the interval (in this case, if it’s just a pure interval, you can also use Padding);
  2. Need to set the background color;
  3. When you need to set rounded corners or borders (ClipRRect can also be rounded);
  4. Need to Align (Align also works);
  5. When the background image needs to be set (Stack can also be used).
2.2.5. Source code analysis of Container
decoration = decoration ?? (color ! = null ? newBoxDecoration(color: color) : null),
Copy the code

It can be seen that the setting of color is changed to decoration for drawing. If both decoration and color are included, an error will be reported.

@override
  Widget build(BuildContext context) {
    Widget current = child;

    if(child == null && (constraints == null || ! constraints.isTight)) { current = newLimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: new ConstrainedBox(constraints: const BoxConstraints.expand())
      );
    }

    if(alignment ! = null) current = newAlign(alignment: alignment, child: current);

    final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration;
    if(effectivePadding ! = null) current = newPadding(padding: effectivePadding, child: current);

    if(decoration ! = null) current = newDecoratedBox(decoration: decoration, child: current);

    if(foregroundDecoration ! = null) { current = newDecoratedBox(
        decoration: foregroundDecoration,
        position: DecorationPosition.foreground,
        child: current
      );
    }

    if(constraints ! = null) current = newConstrainedBox(constraints: constraints, child: current);

    if(margin ! = null) current = newPadding(padding: margin, child: current);

    if(transform ! = null) current = newTransform(transform: transform, child: current);

    return current;
  }
Copy the code

The build function of a Container is not long, and drawing is a linear judgment process, covering widgets layer by layer to implement different styles. The innermost layer is child, and if empty or other constraints, the innermost layer contains a LimitedBox, Then Align, Padding, DecoratedBox, foreground DecoratedBox, ConstrainedBox, Padding, Transform. The source code for Container itself is not complicated, but rather its various layout representations. Keep in mind that if there are no internal constraints, then as large as the parent node can be, if there are internal constraints, then as large as the parent node can be.

2.3 Scaffold

The Scaffold implements the basic Material layout. Scaffold can be used to draw layout control elements that are displayed on a single interface defined in Material. Display drawers (e.g. left side bar), snack bars and bottom buttons (bottom sheets) are provided. Scaffold can be thought of as a container of layouts. This container is where we can draw our user interface.

  1. Source code analysis of Scaffold

  2. The main properties of Scaffold are described

  • AppBar: display a appBar related links at the top of their interface: flutterchina. The club/catalog/Sam…
  • Body: The main content displayed on the current interface
  • FloatingActionButton: A function button defined in Material.
  • PersistentFooterButtons: The buttons that are fixed to the bottom display. Material.google.com/components/…
  • Drawer: sidebar control
  • BottomNavigationBar: Displays the navigation bar button bar at the bottom. See the documentation: Making Flutter Learning bottom menu navigation
  • BackgroundColor: backgroundColor
  • ResizeToAvoidBottomPadding: control interface content body whether or not to the bottom of the layout to avoid being covered, such as when the keyboard display, layout again to avoid being the keyboard cover. The default value is true.
  1. Code sample
class Scaffold extends StatefulWidget {
  /// Creates a visual scaffold for material design widgets.
  const Scaffold({
    Key key,
    this.appBar, // Horizontal layout, usually shown at the top (*)
    this.body, // Contents (*)
    this.floatingActionButton, // Hover button (*)
    this.floatingActionButtonLocation, // Hover button position
    / / suspend button appears in the [floatingActionButtonLocation] / animation
    this.floatingActionButtonAnimator, 
    // Render a set of buttons at the bottom, above [bottomNavigationBar] and below [body]
    this.persistentFooterButtons,
    // A vertical panel, displayed on the left, is initially hidden (*)
    this.drawer,
    this.endDrawer,
    // A series of horizontal buttons appear at the bottom (*)
    this.bottomNavigationBar,
    // Bottom persistent prompt box
    this.bottomSheet,
    // Content background color
    this.backgroundColor,
    // Deprecate, use [resizeToAvoidBottomInset]
    this.resizeToAvoidBottomPadding,
    // Recalculate the layout space size
    this.resizeToAvoidBottomInset,
    // Whether to display bottom, default is true will display to the top status bar
    this.primary = true.//
    this.drawerDragStartBehavior = DragStartBehavior.down,
  }) : assert(primary ! = null),assert(drawerDragStartBehavior ! = null),super(key: key);


Copy the code
  1. Scaffold. Of usage instructions

About the Scaffold of function description: the docs. Flutter. IO/flutter/mat…

When snackbar or Bottom sheet is displayed, the ScaffoldState object needs to be retrieved by calling scaffold. of with the current BuildContext parameter. Then use the ScaffoldState. ShowSnackBar and ScaffoldState. ShowBottomSheet function to display.

Examples from the official source code above. I’ll use the SnackBar notation.

@override
 Widget build(BuildContext context) {
   return new RaisedButton(
     child: new Text('SHOW A SNACKBAR'),
     onPressed: () {
       Scaffold.of(context).showSnackBar(new SnackBar(
         content: new Text('Hello! '))); }); }Copy the code

When that Scaffold is actually created in the same builder, the builder’s BuildContext parameter cannot be used to find that Scaffold because it is “on top” of the returned widget. Return new Scaffold(app: XXXX) is used in the source code. In this case, we provide a new BuildContext with a Builder:

@override
Widget build(BuildContext context) {
  return new Scaffold(
    appBar: new AppBar(
      title: new Text('Demo')
    ),
    body: new Builder(
      // Create an inner BuildContext so that the onPressed methods
      // can refer to the Scaffold with Scaffold.of().
      builder: (BuildContext context) {
        return new Center(
          child: new RaisedButton(
            child: new Text('SHOW A SNACKBAR'),
            onPressed: () {
              Scaffold.of(context).showSnackBar(new SnackBar(
                content: new Text('Hello! '))); },),); },),); }Copy the code

Officially, we can split our builder into multiple Widgets. New BuildContext is introduced to capture the Scaffold.