当前位置: 首页 > news >正文

Flutter - 概览

Hello world

⌘ + shift + p

2025-05-01 20.05.16.png

选择 Empty Application 模板

// 导入Material风格的组件包
// 位置在flutter安装目录/packages/flutter/lib/material.dart
import 'package:flutter/material.dart';void main() {// runApp函数接收MainApp组件并将这个Widget作为根节点runApp(const MainApp());
}class MainApp extends StatelessWidget {const MainApp({super.key});// Describes the part of the user interface represented by this widget.// The framework calls this method when this widget is inserted into the tree in  a given // [BuildContext] and when the dependencies of this widget change // (e.g., an [InheritedWidget] referenced by this widget changes). // This method can potentially be called in every frame and should not have any  // side effects beyond building a widget.Widget build(BuildContext context) {/// An application that uses Material Design./// 使用Material设计的组件,home代表默认页return const MaterialApp(/// The Scaffold is designed to be a top level container for/// a [MaterialApp]. This means that adding a Scaffold/// to each route on a Material app will provide the app with/// Material's basic visual layout structure./// Scaffold,MateriaApp组件的顶层容器,规范样式之类的home: Scaffold(body: Center(  /// 局中显示Hello Worldchild: Text('Hello World'),),),);}
}

build方法用于描述Widget的展示效果,当被添加到上下文的树Widget发生变化时会触发这个方法。因为这个方法是高频操作所以不应该有副作用。

热重载(Hot reload)

Flutter支持热重载,无需重启启动应用的情况下去重新刷新页面。通过将更新代码注入到运行的Dart虚拟机来实现热重载。在虚拟机使用新的字段和函数更新类后,Flutter框架自动重新构建widget。

修改后直接保存/点击调试那里的闪电图标能直接刷新

optimized.gif

有状态(StatefulWidget) + 无状态(StatelessWidget)

Flutter中的一切都是Widget,Widget分为有状态和无状态两种,在 Flutter 中每个页面都是一帧,无状态就是保持在那一帧,而有状态的 Widget 当数据更新时,其实是创建了新的 Widget,只是 State 实现了跨帧的数据同步保存。

比如上面的MainApp是无状态的Widget,而Scaffold是有状态的Widget

class MainApp extends StatelessWidget {
...
}class Scaffold extends StatefulWidget {
...
}

创建新组件时继承有状态还是无状态的Widget取决于是否要管理状态

基础组件

Text

Text 是现实单一样式的文本字符串组件。字符串可能跨多行中断,也可能全部显示在同一行上,取决于布局约束

Widget build(BuildContext context) {return MaterialApp(home:Scaffold(body:Center(// 设置宽度限制为100点child:Container(width: 100,height:30,// 边框decoration: BoxDecoration(border: Border.all()),// TextOverflow.ellipsis 超过部分用...// TextOverflow.clip -- Clip the overflowing text to fix its container. 超出部分换下一行,外部容器会被遮挡// TextOverflow.visible -- Render overflowing text outside of its container. 超出容器部分能渲染child: Text(overflow:TextOverflow.ellipsis, 'Hello world, how are you?')))));}

TextOverflow.ellipsis的效果

2025-05-02 14.56.13.png

TextOverflow.clip 的效果

2025-05-02 14.55.32.png

TextOverflow.visible 的效果

2025-05-02 15.06.07.png

maxLines 控制最大行数
softWrap 控制是否换行

overflowTextOverflow.visible

softWrap: false

2025-05-02 15.04.59.png

softWrap: true

2025-05-02 15.07.44.png

Text.rich

使用Text.rich构造器,Text组件可以在一个段落中展示不同的样式

Widget build(BuildContext context) {return MaterialApp(home: Scaffold(body: Center(child: const Text.rich(TextSpan(text: 'Hello', // default text stylechildren: <TextSpan>[TextSpan(text: ' beautiful ',style: TextStyle(fontStyle: FontStyle.italic),),TextSpan(text: 'world',style: TextStyle(fontWeight: FontWeight.bold),),],),),),),);}

2025-05-02 15.16.39.png

关于Text的交互

GestureDetector widget包装Text,然后在GestureDetector.onTap中处理点击事件。或者使用TextButton来代替

Row,Column,Stack,Container

  • Container: 只有一个子 Widget。默认充满,包含了padding、margin、color、宽高、decoration 等配置
  • Row: 可以有多个子 Widget。水平布局。
  • Column: 可以有多个子 Widget。垂直布局。
  • Stack: 可以有多个子 Widget。 子Widget堆叠在一起。
  • Center: 只有一个子 Widget。只用于居中显示,常用于嵌套child,给child设置居中。
  • Padding: 只有一个子 Widget。只用于设置Padding,常用于嵌套child,给child设置padding。
  • Expanded: 只有一个子 Widget。在 Column 和 Row 中充满。
  • ListView: 可以有多个子Widget,列表布局
// 水平布局
Row(children: [// 图标const IconButton(icon: Icon(Icons.menu),tooltip: 'Navigation menu',onPressed: null, // null disables the button),// Expanded expands its child// to fill the available space.// 填充满2个图标之间的空间Expanded(child: title),// 查询图标const IconButton(icon: Icon(Icons.search),tooltip: 'Search',onPressed: null,),],
)
// 垂直布局
Column(children: [MyAppBar(title: Text('示例标题',style:Theme.of(context) //.primaryTextTheme.titleLarge,),),const Expanded(child: Center(child: Text('容器'))),],
)

2025-05-02 16.10.27.png

要使用material中这些预定义图标,需要将工程中的pubspec.yaml文件里的uses-material-design字段设置为true

使用Material组件

import 'package:flutter/material.dart';void main() {runApp(const MaterialApp(title: 'Flutter Tutorial', home: TutorialHome()));
}class TutorialHome extends StatelessWidget {const TutorialHome({super.key});Widget build(BuildContext context) {// Scaffold is a layout for// the major Material Components.return Scaffold(appBar: AppBar(leading: const IconButton(icon: Icon(Icons.menu),tooltip: 'Navigation menu',onPressed: null,),title: const Text('Material Components'),actions: const [IconButton(icon: Icon(Icons.search),tooltip: 'Search',onPressed: null,),],),// body is the majority of the screen.body: const Center(child: Text('Material!')),floatingActionButton: const FloatingActionButton(tooltip: 'Add', // used by assistive technologiesonPressed: null,child: Icon(Icons.add),),);}
}

2025-05-02 16.31.29.png

使用ScaffoldAppBar替换原来自定义的MyScaffoldMyAppBar

手势处理


Widget build(BuildContext context) {return MaterialApp(home: Scaffold(body: Center(child: GestureDetector(child: Text('Hello world',overflow: TextOverflow.ellipsis,),onTap: ()=> {// 生产环境不要用printprint("123")},)),),);
}

2025-05-02 15.38.53.png

更改小组件以响应输入

UI通常需要对用户的输入进行响应,比如点外卖时根据用户选择菜品计算最后的价格, Flutter中使用StatefulWidgets来处理这种场景。

继承StatefulWidget,重写createState方法

class Counter extends StatefulWidget {const Counter({super.key});// 继承StatefulWidget的类要重写createState()方法,内容返回是_CounterState对象// ``=>`` (胖箭头)简写语法用于仅包含一条语句的函数。该语法在将匿名函数作为参数传递时非常有用State<Counter> createState() => _CounterState();
}

所有的类都隐式定义成了一个接口。因此,任意类都可以作为接口被实现,定义一个继承State并实现Counter类的方法

/// [State] objects are created by the framework by calling the
/// [StatefulWidget.createState] method when inflating a [StatefulWidget] to
/// insert it into the tree. 
/// 在这里当Counter组件被添加到渲染树时,因为也实现了Counter类,所以会调用对应的createState方法。
class _CounterState extends State<Counter> {int _counter = 0;// _ 代表私有方法void _increment() {// 调用setState()通知Flutter状态变化了,然后重新执行build方法实现实时刷新的效果setState(() {_counter++;});}

2025-05-02 17.45.53.png

合并的示例

import 'package:flutter/material.dart';class Product {const Product({required this.name});final String name;
}typedef CartChangedCallback = Function(Product product, bool inCart);class ShoppingListItem extends StatelessWidget {ShoppingListItem({required this.product,required this.inCart,required this.onCartChanged,}) : super(key: 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 const TextStyle(color: Colors.black54,decoration: TextDecoration.lineThrough,);}Widget build(BuildContext context) {return ListTile(// 5. 点击Item时调用传入 onCartChanged 回调,并传入一开始接收的出参数// 比如一开始在订单内inCart传trueonTap: () {onCartChanged(product, inCart);},leading: CircleAvatar(backgroundColor: _getColor(context),child: Text(product.name[0]),),// 4.显示产品订单的样式// 10.根据新参数重新显示样式title: Text(product.name, style: _getTextStyle(context)),);}
}class ShoppingList extends StatefulWidget {// 要求传入 products属性const ShoppingList({required this.products, super.key});final List<Product> products;// 2. 调用_ShoppingListState创建状态对象State<ShoppingList> createState() => _ShoppingListState();
}class _ShoppingListState extends State<ShoppingList> {final _shoppingCart = <Product>{};// 6. 点击触发回调void _handleCartChanged(Product product, bool inCart) {setState(() {// 7. 根据入参进行判断,如果一开始是true,则移除,否则添加,即取反操作if (!inCart) {_shoppingCart.add(product);} else {_shoppingCart.remove(product);}// 8. 通知Flutter 重新执行_ShoppingListState对象的build方法});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Shopping List')),body: ListView(padding: const EdgeInsets.symmetric(vertical: 8),children:// 3. 根据products属性创建ShoppingListItem,并传入产品信息,回调// 9. 再次调用ShoppingListItem并传入新参数widget.products.map((product) {return ShoppingListItem(product: product,inCart: _shoppingCart.contains(product),onCartChanged: _handleCartChanged,);}).toList(),),);}
}void main() {runApp(const MaterialApp(title: 'Shopping App',// 1. 创建ShoppingList对象,并传入Product参数home: ShoppingList(products: [Product(name: 'Eggs'),Product(name: 'Flour'),Product(name: 'Chocolate chips'),],),),);
}

optimized.gif

响应组件的生命周期相关事件

Flutter调用createState方法后,会将state对象添加到渲染树并且调用state对象的initState(),可以重写这个方法中配置动画或准备订阅平台相关的服务,重写方法开始要先调用super.initState。当state对象不再需要时,Flutter会调用对象的dispose方法来执行清理操作,比如取消定时器,取消订阅,同样在重写方法中也要先调用super.dispose

其它

包缓存地址

$ flutter pub get 
# 命令下载的包在~/.pub-cache/hosted

参考

  1. Flutter
  2. Flutter-UI
  3. Flutter - 我给官方提PR,解决run命令卡住问题 😃
  4. Day16 - Flutter - 屏幕适配

相关文章:

  • 深入解析C++11委托构造函数:消除冗余初始化的利器
  • 【CVE-2025-1094】:PostgreSQL 14.15 SQL注入漏洞导致的RCE_ 利用代码和分析
  • AUTOSAR图解==>AUTOSAR_SRS_BusMirroring
  • 贝叶斯算法(Bayesian Algorithms)详解
  • WPF之ProgressBar控件详解
  • SPOJ 11576 TRIP2 - A Famous King’s Trip 【Tarjan+欧拉回路】
  • 【愚公系列】《Manus极简入门》011-习惯养成教练:“习惯塑造师”
  • 2025年- H19-Lc127-48.旋转矩阵(矩阵)---java版
  • Chromium 134 编译指南 - Android 篇:安装构建依赖项(七)
  • Spring、Spring MVC、SpringBoot、Spring Cloud的关系和区别(Spring生态项目关系和区别详解)
  • wpf 输入框 在输入时去除水印
  • Ubuntu环境下如何管理系统中的用户:创建用户、删除用户、修改密码、切换用户、用户组管理
  • C++-Lambda表达式
  • VLM Qwen2.5VL GRPO训练微调 EasyR1 多机多卡训练(2)
  • 代码随想录算法训练营第60期第二十二天打卡
  • Linux diff 命令使用详解
  • 重构之道:识别并替换不合适使用的箭头函数
  • 19.9/Q1,GBD数据库高分文章解读
  • React pros比较机制
  • K8s ConfigMap实战:像设置手机一样管理配置!
  • 一周文化讲座|那些年的年青人
  • 海港负国安主场两连败,五强争冠卫冕冠军开始掉队
  • “五一”假期首日迎出游高峰:火车站人流“堪比春运”,热门景区门票预订量同比增三成
  • 沈晓萍︱严金清:比斯坦因更早获得敦煌文物的无锡名士
  • 强制性国家标准《危险化学品企业安全生产标准化通用规范》发布
  • 赵乐际主持十四届全国人大常委会第十五次会议闭幕会并作讲话