美文网首页Flutter圈子Flutter开发圈Flutter
Flutter 学习记3 - Widget 框架

Flutter 学习记3 - Widget 框架

作者: 三流之路 | 来源:发表于2018-04-10 22:56 被阅读123次

通过 widgets 构建 UI,描述当前的配置和状态,当状态改变时,框架找出前后的变化,以确定底层 Render Tree 要做的最小更改,在内部变成另一个状态来更新外面的 Widget Tree。

一个最简单的 Flutter 程序就是在入口方法 main 中调用 runApp,这个函数将参数的 Widget 作为整个应用的 Widget Tree 的根 Widget,将这个根 Widget 完全覆盖到屏幕上。

Widget 分两种:StatelessWidgetStatefulWidget,主要工作就是实现 build 方法,用于描述更低级的 Widget。

基础的 Widget

  • Text
  • Row, Column: flex 布局,类似 Web 的 flexbox,Android 的 FlexboxLayout
  • Stack:堆叠,类似 Web 的 absolute,也像 Android 的 RelativeLayout,内部使用 Positioned 并控制其在 Stack 内的上下左右的边距
  • Container: 创建一个矩形可见元素,可以通过 BoxDecoration 来做样式,如背景,边框,阴影。可以设置 margin,padding 或者尺寸 size。可以通过矩阵转换变成三维的。

下面是一个示例代码,首先 pubspec.yaml 要配置

flutter:
  uses-material-design: true

新创建的工程默认就是这个配置。

import 'package:flutter/material.dart';

class MyAppBar extends StatelessWidget {
  MyAppBar({this.title});

  // Fields in a Widget subclass are always marked "final".

  final Widget title;

  @override
  Widget build(BuildContext context) {
    return new Container(
      height: 56.0, // in logical pixels
      padding: const EdgeInsets.symmetric(horizontal: 8.0),
      decoration: new BoxDecoration(color: Colors.blue[500]),
      // Row is a horizontal, linear layout.
      child: new Row(
        // <Widget> is the type of items in the list.
        children: <Widget>[
          new IconButton(
            icon: new Icon(Icons.menu),
            tooltip: 'Navigation menu',
            onPressed: null, // null disables the button
          ),
          // Expanded expands its child to fill the available space.
          new Expanded(
            child: title,
          ),
          new IconButton(
            icon: new Icon(Icons.search),
            tooltip: 'Search',
            onPressed: null,
          ),
        ],
      ),
    );
  }
}

class MyScaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Material is a conceptual piece of paper on which the UI appears.
    return new Material(
      // Column is a vertical, linear layout.
      child: new Column(
        children: <Widget>[
          new MyAppBar(
            title: new Text(
              'Example title',
              style: Theme.of(context).primaryTextTheme.title,
            ),
          ),
          new Expanded(
            child: new Center(
              child: new Text('Hello, world!'),
            ),
          ),
        ],
      ),
    );
  }
}

void main() {
  runApp(new MaterialApp(
    title: 'My app', // used by the OS task switcher
    home: new MyScaffold(),
  ));
}

现在还不知道怎么加按钮,将上面的代码放到一个新建的 newroute.dart 文件中,去掉 main 方法,在 main.dart 的 AppBar 上添加一个按钮

new IconButton(icon: new Icon(Icons.add), onPressed: _toOther),
void _toOther() {
  Navigator.of(context).push(
    new MaterialPageRoute(
        builder: (context) {
          return new MyScaffold();
        })
  );
}

要使用另一个文件的 MyScaffold 类,需要 import

import 'newroute.dart';

结果成功跳转。

由于语法还不懂,先简单的分析下:

  1. MyScaffoldStatelessWidget,它的 build 方法返回一个 Material,这就是页面显示的内容。
  2. Material 里只有一个 child,就是一个 Column,一个垂直的线性布局。包含一个 MyAppBarExpanded
  3. ExpandedColumeRowFlex 的 child,意思就占据所有剩下的空间,如果多个 child 都是 Expanded,通过 flex 来指定各自的比例,有些类似 Android 的 weight
  4. MyAppBar 是一个 StatelessWidget,内部是 Container,然后设置它的高度,内边距。child 是 Row,和 Column 类似,只是水平的。左右各一个 IconButton

手势处理

class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: () {
        print('MyButton was tapped!');
        Navigator.of(context).pop(this);
      },
      child: new Container(
        height: 36.0,
        padding: const EdgeInsets.all(8.0),
        margin: const EdgeInsets.symmetric(horizontal: 8.0),
        decoration: new BoxDecoration(
          borderRadius: new BorderRadius.circular(5.0),
          color: Colors.lightGreen[500],
        ),
        child: new Center(
          child: new Text('Engage'),
        ),
      ),
    );
  }
}

当用户触摸 GestureDector 的 child,即这里的 Container,就会调用 onTapGestureDector 可以发现包括 tap,drag 和 scales 事件。

onTap 里让页面返回,效果如下:

IMB_9pMJ4C.GIF

交互

class CounterDisplay extends StatelessWidget {
  CounterDisplay({this.count});

  final int count;

  @override
  Widget build(BuildContext context) {
    return new Text('Count: $count');
  }
}

class CounterIncrementor extends StatelessWidget {
  CounterIncrementor({this.onPressed});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return new RaisedButton(
      onPressed: onPressed,
      child: new Text('Increment'),
    );
  }
}

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => new _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      ++_counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Row(children: <Widget>[
      new CounterIncrementor(onPressed: _increment),
      new CounterDisplay(count: _counter),
      new RaisedButton(
        onPressed: () {
          Navigator.of(context).pop(this);
        },
        child: new Text('返回'),
      ),
    ]);
  }
}

在前一篇里已经遇到了 StatefulWidget,首先 CountercreateState() 返回一个 _CounterState 实例。

看 CounterIncrementor 里的 CounterIncrementor({this.onPressed});,这一句应该是构造方法,在 _CounterState 里调用是 new CounterIncrementor(onPressed: _increment),因此应该是用 _increment 这个函数赋给 CounterIncrementoronPressed,类型是 VoidCallback

/// Signature of callbacks that have no arguments and return no data.
typedef void VoidCallback();

然后当用户点击按压 RaisedButton 时,调用 onPressed 也就是 _increment,将 _counter 加 1,setState 会引起再次 build,于是 CounterDisplay 里的 Text 内容也就变了。

IMB_4rbGFZ.GIF

综合例子

// 一个产品类,一个 name 属性
class Product {
  const Product({this.name});
  final String name;
}

// 这个看后面代码,代表的是一个函数,有点像 C 语言的函数声明,但又不一样,不清楚这个语法
typedef void CartChangedCallback(Product product, bool inCart);

// 一个 StatelessWidget
class ShoppingListItem extends StatelessWidget {
  // 这个构造三个参数,但 product 好像是后面的 product = product 来赋值
  // this.inCart 应该就是传入的参数就覆盖自己的属性 inCart
  ShoppingListItem({Product product, this.inCart, this.onCartChanged})
      : product = product,
        super(key: new ObjectKey(product));

  final Product product;
  final bool inCart;
  final CartChangedCallback onCartChanged; 

  Color _getColor(BuildContext context) {
    // 根据布尔值返回不同的颜色
    return inCart ? Colors.black54 : Theme.of(context).primaryColor;
  }

  TextStyle _getTextStyle(BuildContext context) {
    if (!inCart) return null;

    return new TextStyle(
      color: Colors.black54,
      decoration: TextDecoration.lineThrough,
    );
  }

  @override
  Widget build(BuildContext context) {
    return new ListTile(
      onTap: () {
        // 触摸的时候,inCart 取反,然后回调外界传入的 onCartChanged
        onCartChanged(product, !inCart);
      },
      leading: new CircleAvatar( // 不太清楚干什么的
        backgroundColor: _getColor(context), // 调上面的方法返回不同的背景色
        child: new Text(product.name[0]),
      ),
      title: new Text(product.name, style: _getTextStyle(context)), // 这个 item 的文字
    );
  }
}

这是一个列表的 item,onCartChanged 应该就是一种回调函数,由父 Widget 传入,函数被调用后,父 Widget 更新内部的 State,然后导致利用这个新的 inCart重新创建一个 ShoppingListItem 实例。然后看下这句牛逼哄哄的话:

Although the parent creates a new instance of ShoppingListItem when it rebuilds, that operation is cheap because the framework compares the newly built widgets with the previously built widgets and applies only the differences to the underlying RenderObject.

class ShoppingList extends StatefulWidget {
  ShoppingList({Key key, this.products}) : super(key: key);

  final List<Product> products;

  @override
  _ShoppingListState createState() => new _ShoppingListState();
}

class _ShoppingListState extends State<ShoppingList> {
  Set<Product> _shoppingCart = new Set<Product>();

  // 传给 item 的回调函数,setState 去重新 build
  void _handleCartChanged(Product product, bool inCart) {
    setState(() {
       if (inCart)
        _shoppingCart.add(product);
      else
        _shoppingCart.remove(product);
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Shopping List'),
      ),
      body: new ListView(
        padding: new EdgeInsets.symmetric(vertical: 8.0),
        children: widget.products.map((Product product) {
          return new ShoppingListItem(
            product: product,
            inCart: _shoppingCart.contains(product),
            onCartChanged: _handleCartChanged,
          );
        }).toList(),
      ),
    );
  }
}

// void main() {
//   runApp(new MaterialApp(
//     title: 'Shopping App',
//     home: new ShoppingList(
//       products: <Product>[
//         new Product(name: 'Eggs'),
//         new Product(name: 'Flour'),
//         new Product(name: 'Chocolate chips'),
//       ],
//     ),
//   ));
}

在前面的页面加个按钮跳过来

new RaisedButton(
    onPressed: () {
        Navigator.of(context).push(
            new MaterialPageRoute(
                builder: (context) {
                new ShoppingList(
                    products: <Product>[
                        new Product(name: 'Eggs'),
                        new Product(name: 'Flour'),
                        new Product(name: 'Chocolate chips'),
                    ],
                );
            })  
        );
    },
    ...
)

如果 ShoppingList 的父 Widget 重新构建一个 ShoppingList,但是不会再次调用 createState() 去重新创建一个 _ShoppingListState,而是会复用已经存在的 State 实例。但它会根据新的属性值重新构建,相当于 _ShoppingListState 实例对象只有一个,但是在重新创建 ShoppingList 时会再次调用 State 的 build 方法。嗯,应该是这样。

IMB_OyXeZQ.gif

参考:A Tour of the Flutter Widget Framework

相关文章

  • Flutter 学习记3 - Widget 框架

    通过 widgets 构建 UI,描述当前的配置和状态,当状态改变时,框架找出前后的变化,以确定底层 Render...

  • flutter widget 框架概述

    今天开始,我们开始系统的学习下flutter widget 框架 介绍 Flutter Widget采用现代响应式...

  • Flutter之Widget和Element

    一、Widget Flutter是移动UI框架,Widget组件是UI的基础。 它们都是抽象类,继承Widget,...

  • Flutter widget框架

    Flutter Widget 采用现代响应式框架构建,这是从 React 中获得的灵感,中心思想是用 Widget...

  • flutter widget 控件

    flutter widget控件 学习路径 1.widget 在flutter中UI控件就是wiget 一个wig...

  • Flutter界面开发(二)

    Flutter 视图核心对象简介 Flutter Widget采用现代响应式框架构建,这是从 React 中获得的...

  • 小结

    Flutter框架中获取widget大小的方法取到的值都是逻辑像素.

  • Flutter:Button Widget的使用

    Flutter学习咒语"Flutter一切皆组件 !" Flutter按钮 Material widget库中提供...

  • Flutter Weekly Issue 50

    ?教程 闲鱼 Flutter 图片框架架构演进(超详细) ?插件 epub_viewFlutter widget ...

  • Flutter安装实践 2022-05-12 周四

    三层结构 Flutter 框架从上到下为:框架层、引擎层和嵌入层。 框架层(framework):提供Widget...

网友评论

    本文标题:Flutter 学习记3 - Widget 框架

    本文链接:https://www.haomeiwen.com/subject/rnsohftx.html