写点什么

flutter 系列之:widgets, 构成 flutter 的基石

作者:程序那些事
  • 2022 年 5 月 31 日
  • 本文字数:3952 字

    阅读完需:约 13 分钟

flutter系列之:widgets,构成flutter的基石

简介

flutter 中所有的组件都是由 widgets 组成的,flutter 中有各种各样的 widgets,这些 widgets 构成了 flutter 这个大厦。


那么 flutter 中的 widget 有什么特点呢?我们应该怎么学习 widget 呢? 一起来看看吧。

StatelessWidget 和 StatefulWidget

实时上,flutter 中的 widgets 是受到 React 的启发来实现的。flutter 中的 widget 可以分为 StatefulWidget 和 StatelessWidget,分别代表有状态的 Widgets 和无状态的 Widgets。


有状态和无状态,大家听起来是不是很熟悉,我们在应用程序中也经常会用到有状态的 Bean 和无状态的 Bean。他们的原理和 flutter 的两类 Widget 其实是差不多的。


StatelessWidget 因为是无状态的,所以它只会根据初始传入的配置信息来构建 Widget,因为 Widget 是不可变的,所以 StatelessWidget 创建出来就不会再变化。


对于 StatefulWidget 来说,除了根据初始传入的配置来创建 Widget 之外,它内部还包含了一个 State。这个 State 用来和用户的行为进行交互,从而对 State 中的值进行修改。当 State 被修改后,和其绑定的 Widget 会根据特定的算法进行比较,看是否需要进行重绘,从而将用户的交互反映在用户界面上。


widget 提供了一个 build 方法,build 方法返回一个 Widget,用于生成最后的 RenderObject 对象。


build 方法的定义如下:


Widget build(BuildContext context);
复制代码


但事实上,只有 StatelessWidget 中才有 build 方法。那么 StatefulWidget 为什么没有 build 方法呢?


StatefulWidget 虽然没有 build 方法,但是它有一个 createState 方法用来创建跟它关联的 State:


State createState(); 
复制代码


而这个 build 方法是放在 State 里面的。

StatelessWidget 详解

什么样的组件可以做成无状态的组件呢?那些不需要和用户交互的组件就可以。


flutter 中的无状态 Widget 都有那些呢?


这里列出几个 flutter 中基本和经常使用的 StatelessWidget:


Text: 用来创建文本。


Row 和 Column: 表示的是纵向扩展和横向扩展的行和列。Row 和 Column 是基于 web 的 flexbox 布局。


还有一个基于 web 的绝对定位的布局叫做 Positioned,Positioned 通常是和 Stack 一起使用的。


Stack 就是一个栈的结构,在 Stack 中你可以将一个 widget 放在另外一个 widget 的上面。


Positioned 用在 Stack 中,可以相对于 top, right, bottom 或者 left 边界进行相对定位,非常好用。


另外一个常用的组件就是 Container,它表示的是一个长方形的元素,Container 可以用 BoxDecoration 来修饰,用来表示背景、边框和阴影等。


Container 还可以包含 margins,padding 和尺寸限制等特性。


接下来我们来通过一个具体的例子来说明 StatelessWidget 到底是怎么使用的。


假如我们想构建一个下面样式的界面,该怎么做呢?



这个界面可以分为两部分,上面的一般称之为 appBar,下面的一般叫做 content。


appBar 按列的布局又可以分为三部分,第一部分是一个 IconButton 表示导航菜单,第二部分是一个 Text 表示页面标题,第三部分也是一个 IconButton 表示搜索按钮。 这三部分按照 Row 来进行组合.


那么按照 Flutter 的 widget 的构建原则,我们可以把 appBar 构建成一个 Widget。因为这个 Widget 的行为只跟初始化状态有关,所以可以将其设置成为 StatelessWidget:


class MyAppBar extends StatelessWidget {  const MyAppBar({required this.title, Key? key}) : super(key: key);
final Widget title;
@override Widget build(BuildContext context) { return Container( height: 56.0, // bar的高度 padding: const EdgeInsets.symmetric(horizontal: 8.0), decoration: BoxDecoration(color: Colors.blue[500]), // 按Row来进行布局 child: Row( children: [ const IconButton( icon: Icon(Icons.menu), tooltip: '导航菜单', onPressed: null, // 目前不可点击 ), // Expanded组件,用于填充所有可用的空间 Expanded( child: title, ), const IconButton( icon: Icon(Icons.search), tooltip: '搜索', onPressed: null, ), ], ), ); }}
复制代码


上面的代码中,我们把 Row 包含在一个 Container 中,然后将这个 Container 返回作为 appBar 的实际内容。


UI 下面的部分比较简单, 就是一个居中的 Text。我们将其合和 appBar 合并起来,放在一个 Column 中,按行进行分割:


class MyScaffold extends StatelessWidget {  const MyScaffold({Key? key}) : super(key: key);
@override Widget build(BuildContext context) { return Material( // 构建一个两行的column,一个是bar, 一个是具体的内容 child: Column( children: [ MyAppBar( title: Text( 'StatelessWidget', textAlign: TextAlign.center, style: Theme.of(context) .primaryTextTheme .headline6, ), ), const Expanded( child: Center( child: Text('这是一个Text组件!'), ), ), ], ), ); }}
复制代码


它也是一个 StatelessWidget,在 build 方法中返回了 Material 这个 widget。


然后,我们将 MyScaffold 包装在一个 MaterialApp 中,作为最后返回的 MyApp:


class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);
// 这是应用程序的根widget @override Widget build(BuildContext context) { return MaterialApp( title: '第一个StatelessWidget', theme: ThemeData( primarySwatch: Colors.green, ), home: const SafeArea( child: MyScaffold(), ), ); }}
复制代码


最后在 runApp 方法中运行 MyApp 即可:


void main() {  runApp(const MyApp());}
复制代码

StatefulWidget 详解

上面我们讲解了一个如何使用 StatelessWidget 来构造一个 app 的方法。大家应该对基本的流程有所熟悉。


这里要注意的是,StatelessWidget 并不是说 widget 中不能存储任何变量,如上面的例子所示,MyAppBar 这个 StatelessWidget 其实是包含一个 title 的 Widget,但是这个 widget 是 final 的,也就是说定义过一次之后就不能够再变化,所以叫做 StatelessWidget。


StatefulWidget 和 StatelessWidget 不同的地方在于,StatefulWidget 可以和一个 State 进行关联。State 中可以包含一些可变的属性,这些属性可以跟用户的操作进行交互,从而完成一些比较复杂的功能。


假如我们需要下面的一个界面,界面右下方有一个按钮,点击一次,可以将中间的数字加一。



这是一个很明显的和用户交互的行为。这里我们就可以用到 StatefulWidget。


这里我们创建一个 MyHomePage 的 StatefulWidget,并创建一个_MyHomePageState 的 state 和其进行关联:


class MyHomePage extends StatefulWidget {  const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override State<MyHomePage> createState() => _MyHomePageState();}
复制代码


注意,可变属性是存在和 StatefulWidget 关联的 state 中的,而不是 StatefulWidget 本身。


所以我们需要在_MyHomePageState 中定义一个 int 的_counter 变量,用来存储用户点击次数。然后定义一个_incrementCounter 用来对_counter 进行累加。


在_incrementCounter 需要调用 setState 方法用来对 State 的状态进行刷新。


  int _counter = 0;
void _incrementCounter() { setState(() { _counter++; }); }
复制代码


然后在 State 中的 build 方法中就可以返回对应 UI 的 Widget 了。这里我们使用 Scaffold 组件,这个组件自带了 appBar 和 body:


Widget build(BuildContext context) {
return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( '按钮被点了:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), const Text( '次', ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); }
复制代码


这里 body 中我们选择使用 Center 组件用来展示内容信息。而浮动的按钮则使用 FloatingActionButton,它的 onPressed 方法会触发我们前面写的_incrementCounter 方法,用来将_counter 加一。


最后将我们构建的组件传入 MaterialApp 中,如下所示:


class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);
// 根Widget @override Widget build(BuildContext context) { return MaterialApp( title: '第一个StatefulWidget', theme: ThemeData( primarySwatch: Colors.green, ), home: const MyHomePage(title: 'StatefulWidget'), ); }}
复制代码

总结

以上,我们简单的讲解了 StatelessWidget 和 StatefulWidget 的简单使用情况。后续我们将会这些组件进行深入,敬请期待。


本文的代码可以参考:https://github.com/ddean2009/learn-flutter, 觉得好的话,请点个赞,谢谢。


更多内容请参考 http://www.flydean.com/02-flutter-widget/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 5
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
flutter系列之:widgets,构成flutter的基石_flutter_程序那些事_InfoQ写作社区