Widget中的State-学习笔记
在Flutter开发中,Widget是构建用户界面的核心组件,主要分为StatelessWidget和StatefulWidget两种类型。StatelessWidget适用于静态视图,而StatefulWidget则用于动态交互。虽然StatefulWidget的功能更强大,但选择合适的Widget类型对于优化应用性能至关重要。理解声明式编程范式是关键,它让开发者更专注于数据管理,而非繁琐的UI操作。
比如,构建一个简单的文本显示界面时,使用StatelessWidget即可。但如果需要根据用户输入动态更新文本,就需要StatefulWidget。此外,掌握基础Widget如Text、Row、Column,可以帮助你高效构建复杂的布局。
记住,合理选择Widget类型,并熟练使用声明式编程,不仅能让你的代码更简洁,还能提升应用的运行效率。花时间理解这些概念,会在长期开发中节省更多时间,享受Flutter带来的开发乐趣。
Widget 有 StatelessWidget 和 StatefulWidget 两种类型。StatefulWidget 应对有交互、需要动态变化视觉效果的场景,而 StatelessWidget 则用于处理静态的、无状态的视图展示。StatefulWidget 的场景已经完全覆盖了 StatelessWidget,因此我们在构建界面时,往往会大量使用 StatefulWidget 来处理静态的视图展示需求,看起来似乎也没什么问题。
UI 编程范式
要想理解 StatelessWidget 与 StatefulWidget 的使用场景,我们首先需要了解,在 Flutter 中,如何调整一个控件(Widget)的展示样式,即 UI 编程范式。
如果你有过原生系统(Android、iOS)或原生 JavaScript 开发经验的话,应该知道视图开发是命令式的,需要精确地告诉操作系统或浏览器用何种方式去做事情。比如,如果我们想要变更界面的某个文案,则需要找到具体的文本控件并调用它的控件方法命令,才能完成文字变更。
下述代码分别展示了在 Android、iOS 及原生 Javascript 中,如何将一个文本控件的展示文案更改为 Hello World:
// Android设置某文本控件展示文案为Hello World
TextView textView = (TextView) findViewById(R.id.txt);
textView.setText("Hello World");// iOS设置某文本控件展示文案为Hello World
UILabel *label = (UILabel *)[self.view viewWithTag:1234];
label.text = @"Hello World";// 原生JavaScript设置某文本控件展示文案为Hello World
document.querySelector("#demo").innerHTML = "Hello World!";
与此不同的是,Flutter 的视图开发是声明式的,其核心设计思想就是将视图和数据分离,这与 React 的设计思路完全一致。
如果要实现同样的需求,则要稍微麻烦点:除了设计好 Widget 布局方案之外,还需要提前维护一套文案数据集,并为需要变化的 Widget 绑定数据集中的数据,使 Widget 根据这个数据集完成渲染。
但是,当需要变更界面的文案时,我们只要改变数据集中的文案数据,并通知 Flutter 框架触发 Widget 的重新渲染即可。这样一来,开发者将无需再精确关注 UI 编程中的各个过程细节,只要维护好数据集即可。比起命令式的视图开发方式需要挨个设置不同组件(Widget)的视觉属性,这种方式要便捷得多。
Introduction to widgets
(Introduction to widgets | Flutter)
Flutter 小部件是使用从 React 中汲取灵感的现代框架构建的。 中心思想是使用小部件构建 UI。 小部件描述了它们的视图在给定当前配置和状态的情况下应该是什么样子。 当小部件的状态发生变化时,小部件会重建其描述,框架将其与之前的描述进行比较,以确定底层渲染树从一种状态转换到另一种状态所需的最小变化。
最小的 Flutter 应用程序只需使用一个小部件调用 runApp() 函数:
import 'package:flutter/material.dart';void main() {runApp(const Center(child: Text('Hello World',textDirection: TextDirection.ltr,),));
}
runApp() 函数获取给定的 Widget 并使其成为小部件树的根。 在此示例中,小部件树由两个小部件组成,中心小部件及其子部件文本小部件。 该框架强制根部件覆盖屏幕,这意味着文本“Hello, world”最终在屏幕上居中。 在这种情况下需要指定文本方向; 当使用 MaterialApp 小部件时,这会为您处理,如后文所示。
在编写应用程序时,通常会编写新的小部件,这些小部件是 StatelessWidget 或 StatefulWidget 的子类,具体取决于您的小部件是否管理任何状态。 小部件的主要工作是实现 build() 函数,该函数根据其他较低级别的小部件来描述小部件。 该框架依次构建这些小部件,直到该过程在代表底层 RenderObject 的小部件中触底,该渲染对象计算并描述小部件的几何形状。
Basic widgets Introduction to widgets | Flutter
Flutter 自带了一套强大的基础 widgets,其中常用的有:
Text
文本小部件允许在应用程序中创建一系列样式文本。Text class - widgets library - Dart API (flutter.dev)
具有单一样式的一系列文本。 文本小部件显示一串具有单一样式的文本。 根据布局约束,字符串可能会跨多行显示,也可能全部显示在同一行上。
样式参数是可选的。 省略时,文本将使用最接近的封闭 DefaultTextStyle 的样式。 如果给定样式的 TextStyle.inherit 属性为 true(默认值),则给定样式将与最接近的封闭 DefaultTextStyle 合并。 这种合并行为很有用,例如,在使用默认字体系列和大小时将文本设为粗体。
此示例说明如何使用溢出设置为 TextOverflow.ellipsis 的文本小部件显示文本。
import 'package:flutter/material.dart';void main() {runApp(const Center(child: Text('Hello, David! How are you?',textAlign: TextAlign.center,overflow: TextOverflow.ellipsis,textDirection: TextDirection.ltr,style: TextStyle(fontWeight: FontWeight.bold),),));
}
import 'package:flutter/material.dart';void main() {runApp(const Center(child: Text('Hello, David! How are you How are you How are you How are you How are you How are you How are you?',textAlign: TextAlign.center,overflow: TextOverflow.ellipsis,textDirection: TextDirection.ltr,style: TextStyle(fontWeight: FontWeight.bold),),));
}
使用 Text.rich 构造函数,Text 小部件可以显示具有不同样式的 TextSpans 的段落。 下面的示例显示“Hello beautiful world”,每个单词都有不同的样式。
void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Sample App',theme: ThemeData(primarySwatch: Colors.blue),home: const RichTextDemo(),);}
}class RichTextDemo extends StatelessWidget {const RichTextDemo({super.key});@overrideWidget build(BuildContext context) {return Container(alignment: Alignment.center,child: const Text.rich(TextSpan(text: 'Hello\\n',style: TextStyle(fontWeight: FontWeight.normal),children: <TextSpan>[TextSpan(text: 'beautiful\\n',style: TextStyle(fontStyle: FontStyle.italic)),TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold))]),),);}
}
互动性
要使 Text 对触摸事件做出反应,请将其包装在带有 GestureDetector.onTap 处理程序的 GestureDetector 小部件中。
在 Material Design 应用程序中,请考虑改用 TextButton,或者如果这不合适,至少使用 InkWell 而不是 GestureDetector。
要使文本部分具有交互性,请使用 RichText 并将 TapGestureRecognizer 指定为文本相关部分的 TextSpan.recognizer。
选择
默认情况下无法选择文本。 要使文本可选择,可以用 SelectionArea 小部件包装子树。 要从选择中排除 SelectionArea 下的子树的一部分,也可以用 SelectionContainer.disabled 包裹子树的那一部分。
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});static const String _title = 'Flutter Code Sample';@overrideWidget build(BuildContext context) {return MaterialApp(title: _title,home: Scaffold(appBar: AppBar(title: const Text(_title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: const <Widget>[Text('Selectable text'),SelectionContainer.disabled(child: Text('Non-selectable text')),Text('Selectable text'),],),),),);}
}
RichText,它使您可以更好地控制文本样式。
DefaultTextStyle,它设置文本小部件的默认样式。
SelectableRegion,它提供了选择系统的概览。
行、列
Basic Flutter layout concepts | Flutter
这些 flex 小部件可让您在水平(行)和垂直(列)方向上创建灵活的布局。 这些对象的设计基于网络的 flexbox