移动端混合开发Flutter入坑widgets介绍

Posted IT科技互联

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动端混合开发Flutter入坑widgets介绍相关的知识,希望对你有一定的参考价值。

widgets介绍

  • 编写“Hello World”

  • 基础 Widget

  • 使用 Materia组件

  • 手势处理

  • Widget状态响应处理

  • 购物车综合例子

一、编写“Hello World”

最小的 Flutter应用程序只需调用 runApp()函数使用 widget

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


  2. void main() {

  3.  runApp(

  4.    Center(

  5.      child: Text(

  6.        'Hello, world!',

  7.        textDirection: TextDirection.ltr,

  8.      ),

  9.    ),

  10.  );

  11. }

runApp()函数返回 Widget并使其成为 Widget树的root父级。 在此示例中, Widget树由两个 Widget小部件组成,即 CenterWidget小部件及其子窗口 TextWidget小部件。 框架强制root父级 widget覆盖屏幕,这意味着Text Widget “Hello,world”最终以屏幕为中心。 在此实例中需要指定文本方向 textDirection; 当使用 MaterialAppwidget时,这将由你负责。

二、基础widgets

Flutter附带了一套功能强大的基础widget,其中以下是非常常用的widget:

  • Text: 创建一系列样式文本。

  • Row, 水平(行)和垂直(列)方向上创建灵活的布局。 它的设计基于web的flexbox布局模型。

  • Stack: Stack widget不是线性定向(水平或垂直),而是允许您按照绘制顺序将widget堆叠在一起。 然后,您可以在堆栈的子项上使用Positioned(坐标) widget,以相对于堆栈的顶部,右侧,底部或左侧边缘定位它们。 堆栈基于Web的绝对定位布局模型。

  • Container: Container widget允许您创建矩形可视元素。 容器可以使用BoxDecoration进行装饰,例如背景,边框或阴影。 Container也可以应用其大小的边距,填充和约束。 另外,可以使用矩阵在三维空间中变换容器。

下面是组合多种widget的例子:

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


  2. class MyAppBar extends StatelessWidget {

  3.  MyAppBar({this.title});


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


  5.  final Widget title;


  6.  @override

  7.  Widget build(BuildContext context) {

  8.    return Container(

  9.      height: 88.0, // in logical pixels

  10.      padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 44.0),

  11.      decoration: BoxDecoration(color: Colors.blue[500]),

  12.      // Row is a horizontal, linear layout.

  13.      child: Row(

  14.        // <Widget> is the type of items in the list.

  15.        children: <Widget>[

  16.          IconButton(

  17.            icon: Icon(Icons.menu),

  18.            tooltip: 'Navigation menu',

  19.            onPressed: null, // null disables the button

  20.          ),

  21.          // Expanded expands its child to fill the available space.

  22.          Expanded(

  23.            child: title,

  24.          ),

  25.          IconButton(

  26.            icon: Icon(Icons.search),

  27.            tooltip: 'Search',

  28.            onPressed: null,

  29.          ),

  30.        ],

  31.      ),

  32.    );

  33.  }

  34. }


  35. class MyScaffold extends StatelessWidget {

  36.  @override

  37.  Widget build(BuildContext context) {

  38.    // Material is a conceptual piece of paper on which the UI appears.

  39.    return Material(

  40.      // Column is a vertical, linear layout.

  41.      child: Column(

  42.        children: <Widget>[

  43.          MyAppBar(

  44.            title: Text(

  45.              'Example title',

  46.              style: Theme.of(context).primaryTextTheme.title,

  47.            ),

  48.          ),

  49.          Expanded(

  50.            child: Center(

  51.              child: Text('Hello, world!'),

  52.            ),

  53.          ),

  54.        ],

  55.      ),

  56.    );

  57.  }

  58. }


  59. void main() {

  60.  runApp(MaterialApp(

  61.    title: 'My app', // used by the OS task switcher

  62.    home: MyScaffold(),

  63.  ));

  64. }

移动端混合开发Flutter入坑(二)widgets介绍

三、使用Material组件

使用Material组件可以快速搭建app页面架构, Material应用程序以MaterialApp为root 父级widget,该widget在应用程序的根目录下构建了许多有用的widget,包括Navigator,它管理“路由”。 Navigator可让您在应用程序的屏幕之间平滑过渡。 使用MaterialApp Widget是一个构建app页面的好方法。

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


  2. void main() {

  3.  runApp(MaterialApp(

  4.    title: 'Flutter Tutorial',

  5.    home: TutorialHome(),

  6.  ));

  7. }


  8. class TutorialHome extends StatelessWidget {

  9.  @override

  10.  Widget build(BuildContext context) {

  11.    // Scaffold is a layout for the major Material Components.

  12.    return Scaffold(

  13.      appBar: AppBar(

  14.        leading: IconButton(

  15.          icon: Icon(Icons.menu),

  16.          tooltip: 'Navigation menu',

  17.          onPressed: null,

  18.        ),

  19.        title: Text('Example title'),

  20.        actions: <Widget>[

  21.          IconButton(

  22.            icon: Icon(Icons.search),

  23.            tooltip: 'Search',

  24.            onPressed: null,

  25.          ),

  26.        ],

  27.      ),

  28.      // body is the majority of the screen.

  29.      body: Center(

  30.        child: Text('Hello, world!'),

  31.      ),

  32.      floatingActionButton: FloatingActionButton(

  33.        tooltip: 'Add', // used by assistive technologies

  34.        child: Icon(Icons.add),

  35.        onPressed: null,

  36.      ),

  37.    );

  38.  }

  39. }

移动端混合开发Flutter入坑(二)widgets介绍

四、手势处理

基于上一段代码自定义一个带相应效果的button:

 
   
   
 
  1. class MyButton extends StatelessWidget {

  2.  @override

  3.  Widget build(BuildContext context) {

  4.    return GestureDetector(

  5.      onTap: () {

  6.        print('MyButton was tapped!');

  7.      },

  8.      child: Container(

  9.        height: 36.0,

  10.        padding: const EdgeInsets.all(8.0),

  11.        margin: const EdgeInsets.symmetric(horizontal: 8.0),

  12.        decoration: BoxDecoration(

  13.          borderRadius: BorderRadius.circular(5.0),

  14.          color: Colors.lightGreen[500],

  15.        ),

  16.        child: Center(

  17.          child: Text('Engage'),

  18.        ),

  19.      ),

  20.    );

  21.  }

  22. }

移动端混合开发Flutter入坑(二)widgets介绍

GestureDetectorWidget没有可视化,是是检测用户做出的手势。 当用户点击 Container时, GestureDetector会调用其 onTap回调,在这种情况下会向控制台打印一条消息。 您可以使用 GestureDetector检测各种输入手势,包括点击,拖动和缩放。

许多 Widget使用 GestureDetector为其他Widget提供可选的回调。 例如, IconButtonRaisedButtonFloatingActionButtonWidget具有 onPressed回调,当用户点击小部件时会触发这些回调。

五、widget状态响应处理

到目前为止,我们只使用了 StatelessWidgetStatelessWidget从其父 widget接收参数,它们存储在成员变量中。 当要求构建 widget时,它使用这些存储的值为它创建的widget派生新的参数。

为了构建更复杂的 widget 例如:

  • 以更有趣的方式对用户输入做出反应

  • 应用程序通常带有一些状态。

Flutter使用 StatefulWidgets实现这个方法。 StatefulWidgets是特殊的 Widget,它们知道如何生成 State对象,然后用于保存状态。 考虑下面基本的例子:

 
   
   
 
  1. class Counter extends StatefulWidget {

  2.  // This class is the configuration for the state. It holds the

  3.  // values (in this case nothing) provided by the parent and used by the build

  4.  // method of the State. Fields in a Widget subclass are always marked "final".


  5.  @override

  6.  _CounterState createState() => _CounterState();

  7. }


  8. class _CounterState extends State<Counter> {

  9.  int _counter = 0;


  10.  void _increment() {

  11.    setState(() {

  12.      // This call to setState tells the Flutter framework that

  13.      // something has changed in this State, which causes it to rerun

  14.      // the build method below so that the display can reflect the

  15.      // updated values. If we changed _counter without calling

  16.      // setState(), then the build method would not be called again,

  17.      // and so nothing would appear to happen.

  18.      _counter++;

  19.    });

  20.  }


  21.  @override

  22.  Widget build(BuildContext context) {

  23.    // This method is rerun every time setState is called, for instance

  24.    // as done by the _increment method above.

  25.    // The Flutter framework has been optimized to make rerunning

  26.    // build methods fast, so that you can just rebuild anything that

  27.    // needs updating rather than having to individually change

  28.    // instances of widgets.

  29.    return Row(

  30.      children: <Widget>[

  31.        RaisedButton(

  32.          onPressed: _increment,

  33.          child: Text('Increment'),

  34.        ),

  35.        Text('Count: $_counter'),

  36.      ],

  37.    );

  38.  }

  39. }

移动端混合开发Flutter入坑(二)widgets介绍

可能你们会疑惑为什么 StatefulWidgetState是单独的声明。 在 Flutter中,这两种类型的对象具有不同的生命周期。 Widget是临时对象,用于构造应用程序当前状态的表示。 另一方面,状态对象在调用 build()之间是持久的,允许它们缓存信息。

以下稍微复杂的示例显示了它在实践中的工作原理:

 
   
   
 
  1. class CounterDisplay extends StatelessWidget {

  2.  CounterDisplay({this.count});


  3.  final int count;


  4.  @override

  5.  Widget build(BuildContext context) {

  6.    return Text('Count: $count');

  7.  }

  8. }


  9. class CounterIncrementor extends StatelessWidget {

  10.  CounterIncrementor({this.onPressed});


  11.  final VoidCallback onPressed;


  12.  @override

  13.  Widget build(BuildContext context) {

  14.    return RaisedButton(

  15.      onPressed: onPressed,

  16.      child: Text('Increment'),

  17.    );

  18.  }

  19. }


  20. class Counter extends StatefulWidget {

  21.  @override

  22.  _CounterState createState() => _CounterState();

  23. }


  24. class _CounterState extends State<Counter> {

  25.  int _counter = 0;


  26.  void _increment() {

  27.    setState(() {

  28.      ++_counter;

  29.    });

  30.  }


  31.  @override

  32.  Widget build(BuildContext context) {

  33.    return Row(children: <Widget>[

  34.      CounterIncrementor(onPressed: _increment),

  35.      CounterDisplay(count: _counter),

  36.    ]);

  37.  }

  38. }

注意我们是如何创建两个新的 StatelessWidget,干净地分离 显示计数器(CounterDisplay更改计数器(CounterIncrementor)的问题。 尽管最终结果与前面的示例相同,但是责任分离允许将更大的复杂性封装在各个 widget中,同时保持父级的简单性。

六、购物车综合例子

以下是一个更完整的购物车示例,汇集了上面介绍的知识点, 显示出售的各种产品,并维护购物车的库存。

  • 首先定义表示类ShoppingListItem:

 
   
   
 
  1. class Product {

  2.  const Product({this.name});

  3.  final String name;

  4. }


  5. typedef void CartChangedCallback(Product product, bool inCart);


  6. class ShoppingListItem extends StatelessWidget {

  7.  ShoppingListItem({Product product, this.inCart, this.onCartChanged})

  8.      : product = product,

  9.        super(key: ObjectKey(product));


  10.  final Product product;

  11.  final bool inCart;

  12.  final CartChangedCallback onCartChanged;


  13.  Color _getColor(BuildContext context) {

  14.    // The theme depends on the BuildContext because different parts of the tree

  15.    // can have different themes.  The BuildContext indicates where the build is

  16.    // taking place and therefore which theme to use.


  17.    return inCart ? Colors.black54 : Theme.of(context).primaryColor;

  18.  }


  19.  TextStyle _getTextStyle(BuildContext context) {

  20.    if (!inCart) return null;


  21.    return TextStyle(

  22.      color: Colors.black54,

  23.      decoration: TextDecoration.lineThrough,

  24.    );

  25.  }


  26.  @override

  27.  Widget build(BuildContext context) {

  28.    return ListTile(

  29.      onTap: () {

  30.        onCartChanged(product, !inCart);

  31.      },

  32.      leading: CircleAvatar(

  33.        backgroundColor: _getColor(context),

  34.        child: Text(product.name[0]),

  35.      ),

  36.      title: Text(product.name, style: _getTextStyle(context)),

  37.    );

  38.  }

  39. }

ShoppingListItemWidget遵循 StatelessWidget的通用模式。 它将它在构造函数中接收的值存储在最终成员变量中,然后在构建函数中使用它们。 例如, inCart bool值在两个视觉外观之间切换:一个使用当前主题的主要颜色,另一个使用灰色。

当用户点击列表item时, Widget不会直接修改其 inCart值。 相反, Widget调用从其父 Wdiget接收的 onCartChanged函数。 此模式允许您在 Widget层次结构中存储更高的状态,这会导致状态持续更长时间。 在极端情况下,传递给 runApp()Widget上存储的状态在应用程序的生命周期内持续存在。

当父级接收到onCartChanged回调时,父级会更新其内部状态,从而触发父级重建并使用新的 inCart值创建 ShoppingListItem的新实例。 尽管父级在重建时会创建 ShoppingListItem的新实例,但该操作低耗,因为该框架将新构建的 Widget与先前构建的 Widget进行比较,并仅将差异应用于基础 RenderObject

 
   
   
 
  1. class ShoppingList extends StatefulWidget {

  2.  ShoppingList({Key key, this.products}) : super(key: key);


  3.  final List<Product> products;


  4.  // The framework calls createState the first time a widget appears at a given

  5.  // location in the tree. If the parent rebuilds and uses the same type of

  6.  // widget (with the same key), the framework re-uses the State object

  7.  // instead of creating a new State object.


  8.  @override

  9.  _ShoppingListState createState() => _ShoppingListState();

  10. }


  11. class _ShoppingListState extends State<ShoppingList> {

  12.  Set<Product> _shoppingCart = Set<Product>();


  13.  void _handleCartChanged(Product product, bool inCart) {

  14.    setState(() {

  15.      // When a user changes what's in the cart, we need to change _shoppingCart

  16.      // inside a setState call to trigger a rebuild. The framework then calls

  17.      // build, below, which updates the visual appearance of the app.


  18.      if (inCart)

  19.        _shoppingCart.add(product);

  20.      else

  21.        _shoppingCart.remove(product);

  22.    });

  23.  }


  24.  @override

  25.  Widget build(BuildContext context) {

  26.    return Scaffold(

  27.      appBar: AppBar(

  28.        title: Text('Shopping List'),

  29.      ),

  30.      body: ListView(

  31.        padding: EdgeInsets.symmetric(vertical: 8.0),

  32.        children: widget.products.map((Product product) {

  33.          return ShoppingListItem(

  34.            product: product,

  35.            inCart: _shoppingCart.contains(product),

  36.            onCartChanged: _handleCartChanged,

  37.          );

  38.        }).toList(),

  39.      ),

  40.    );

  41.  }

  42. }


  43. void main() {

  44.  runApp(MaterialApp(

  45.    title: 'Shopping App',

  46.    home: ShoppingList(

  47.      products: <Product>[

  48.        Product(name: 'Eggs'),

  49.        Product(name: 'Flour'),

  50.        Product(name: 'Chocolate chips'),

  51.      ],

  52.    ),

  53.  ));

  54. }

移动端混合开发Flutter入坑(二)widgets介绍

ShoppingList类继承了 StatefulWidget,这意味着此 Widget存储可变状态。 当 ShoppingListWidget首次插入树中时,框架会调用 createState函数来创建 _ShoppingListState的新实例,以与树中的该位置相关联。 (请注意,State的子类通常以前导下划线命名,表示它们是私有实现细节。)当此 Widget的父窗口重建时,父窗口创建 ShoppingList的新实例,但框架重用已在树中的 _ShoppingListState实例 而不是再次调用 createState

欢迎关注我的公众号,了解更多技术博文。


以上是关于移动端混合开发Flutter入坑widgets介绍的主要内容,如果未能解决你的问题,请参考以下文章

移动端混合开发Flutter入坑环境搭建

即将开源 | 让Flutter真正支持View级别的混合开发

周三晚7点 | Flutter入坑指南:新一代的跨平台移动APP开发技术

一不小心入坑了Flutter

flutter系列之:移动端手势的具体使用

做混合的话Uniapp和Flutter我应该学哪个啊?