动易网站风格免费下载淘宝网站内站建设
使用fvm管理flutter版本
如果你有使用多版本flutter的需求,那么fvm将会给你提供较大的帮助。下面我列举一下mac + flutter3.35.2的版本的操作命令,完成之后,你将可以随意切换flutter版本
# 下载fvm相关的依赖
brew tap leoafarias/fvm
brew install fvm# 检查版本号
fvm --version
# 3.2.1# 下载flutter, 并使用
fvm install 3.35.2 #或者直接下载具体版本号也行
fvm use 3.35.2# 检查flutter版本号
fvm flutter --version
# 3.35.2
这里由于笔者已经全局安装一个公司私域的flutter, 并且配置了全局的path, fvm目前识别不到已经安装的flutter,由于不想影响已经安装的全局私域flutter,这里可以在flutter命令前直接加fvm,即可使用到fvm安装的官方flutter,如果不是我这种特殊情况的话,直接使用flutter命令理论是可以切到fvm设置的global源的。
创建你的第一flutter应用
前置步骤
创建flutter之前,你需要配置你的开发环境。具体的步骤官网做了较为详细的概括,这里将不再赘述,主要分为几个步骤。
- 下载flutter sdk, vscode配置flutter相关插件
- 下载配置android studio,下载jdk
- 下载配置xcode
- 测试手机开启授权
创建flutter应用
fvm flutter create test_app
cd test_app
fvm flutter run
选择浏览器打开,第一个flutter demo就创建完成了

源代码剖析
结构列表总结
在test_app这个根目录下,大约有十几个子目录,如下。
| 名称 | 类型 | 主要作用 |
|---|---|---|
| test_app | 目录 | 项目根目录 |
| .dart_tool | 目录 | Dart 工具链配置与缓存 |
| .idea | 目录 | IDE 配置 |
| android | 目录 | Android 平台原生代码 |
| build | 目录 | 构建产物(可删除) |
| ios | 目录 | iOS 平台原生代码 |
| lib | 目录 | 核心 Dart 应用代码 |
| linux | 目录 | Linux 桌面端原生代码 |
| macos | 目录 | macOS 桌面端原生代码 |
| test | 目录 | 测试代码 |
| web | 目录 | Web 平台代码 |
| windows | 目录 | Windows 桌面端原生代码 |
| .gitignore | 文件 | Git 忽略规则 |
| .metadata | 文件 | Flutter 工具元数据 |
| analysis_options.yaml | 文件 | 静态代码分析规则配置 |
| pubspec.lock | 文件 | 依赖包精确版本锁定 |
| pubspec.yaml | 文件 | 项目依赖与元数据配置(非常重要) |
| README.md | 文件 | 项目说明文档 |
| test_app.iml | 文件 | IntelliJ 模块配置 |
作为入门,我们可以重点关注pubspec.yaml、web、和lib
pubspec.yaml目录
去除了多余的代码之后,整体的结果如下
name: test_app
description: "A new Flutter project."
publish_to: 'none'
version: 1.0.0+1environment:sdk: ^3.9.0dependencies:flutter:sdk: fluttercupertino_icons: ^1.0.8dev_dependencies:flutter_test:sdk: flutterflutter_lints: ^5.0.0flutter:uses-material-design: true
name字段
表示当前flutter应用的包名,是一个最基本的字段
我们在导入其他文件时,就需要使用如下方式,如果包名发生变化的话,相应的路径也需要发生变化
import 'package:flutter_demo/listview_demo/listview_demo.dart';
publish_to
此属性意为包发布到哪里去
none:表示此包不发布;- 也可以指定发布的服务器,如果删除此项配置,那么默认发布到
pub.dev
version
此属性表示当前工程的版本,分为应用程序的版本和内部版本号,格式为x.x.x+x,比如1.0.0+1,称为语义版本号;
+号前面的叫做version number;+号后面的叫做build number;
在test_app/android/app/build.gradle.kts这个文件里可以看到安卓打包定版本的具体逻辑
environment
可以配置Flutter和Dart版本
dependencies
dependencies:flutter:sdk: fluttercupertino_icons: ^1.0.8
添加我们用到的第三方的sdk
sdk: flutter意为默认获取flutter的最新版本,也就是我们机器上的flutter版本,我们也可以在此处添加version来指定flutter的版本;cupertino_icons:给应用程序添加Cupertino图标的,一般用于iOS;
其实这个本质上跟js项目的生产依赖是一样的
dev_dependencies
开发依赖,只有运行时才会用到
flutter
Flutter相关的配置
# 确保我们的应用程序中包含Material Icons字体,以使我们能够使用material Icons类中的图标;
uses-material-design: true
我们当资源的配置也是在这个配置下进行设置:
assets:配置图片;fonts:配置字体;plugin:该配置只存在于插件项目中,用来配置适配的平台,一般不要修改;如需添加新平台,直接添加即可;
web目录

主要包含了一些生成web页面的静态资源和模版信息
lib目录
flutter项目的源代码

main.dart整个应用的入口文件
完整的代码信息如下
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {int _counter = 0;void _incrementCounter() {setState(() {_counter++;});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:'),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),),);}
}
导入依赖
import 'package:flutter/material.dart';
导入了 Material UI 组件库。Material(opens new window)是一种标准的移动端和web端的视觉设计语言, Flutter 默认提供了一套丰富的 Material 风格的UI组件。
应用入口
void main() {runApp(const MyApp());
}
Flutter 应用中 main 函数为应用程序的入口。main 函数中调用了runApp 方法,它的功能是启动Flutter应用。runApp它接受一个 Widget参数,在本示例中它是一个MyApp对象,MyApp()是 Flutter 应用的根组件。
也可以简写成单行函数
void main() => runApp(MyApp());
应用代码结构
class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}
}
MyApp类代表 Flutter 应用,它继承了StatelessWidget类,这也就意味着应用本身也是一个widget。- 在 Flutter 中,大多数东西都是 widget,包括对齐(Align)、填充(Padding)、手势处理(GestureDetector)等,它们都是以 widget 的形式提供。
- Flutter 在构建页面时,会调用组件的
build方法,widget 的主要工作是提供一个 build() 方法来描述如何构建 UI 界面(通常是通过组合、拼装其他基础 widget )。 MaterialApp是Material 库中提供的 Flutter APP 框架,通过它可以设置应用的名称、主题、语言、首页及路由列表等。MaterialApp也是一个 widget。home为 Flutter 应用的首页,它也是一个 widget。
具体代码解析
MyHomePage
class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;// 重写createState方法,创建与这个Widget关联的状态类@overrideState<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {// ...
}
MyHomePage 是应用的首页,它继承自StatefulWidget类,表示它是一个有状态的组件(Stateful widget)。
- Stateful widget 可以拥有状态,这些状态在 widget 生命周期中是可以变的,而 Stateless widget 是不可变的。
- Stateful widget 至少由两个类组成:
-
- 一个
StatefulWidget类。 - 一个
State类;StatefulWidget类本身是不变的,但是State类中持有的状态在 widget 生命周期中可能会发生变化。 _MyHomePageState类是MyHomePage类对应的状态类。这里可以看到,和MyApp类不同,MyHomePage类中并没有build方法,取而代之的是,build方法被挪到了_MyHomePageState方法中,至于为什么,后面会进行解读
- 一个
const MyHomePage({super.key, required this.title});
构造函数,用于初始化一个名为 MyHomePage 的页面(Widget)。
State类
_MyHomePageState在就是一个state类,在MyHomePage里面被createState出来,里面主要有两部分内容, 一是定义了一个计数器状态,再定义一个状态自增函数,当按钮点击时,会调用此函数,该函数的作用是先自增_counter,然后调用setState 方法。setState方法的作用是通知 Flutter 框架,有状态发生了改变,Flutter 框架收到通知后,会执行 build 方法来根据新的状态重新构建界面, Flutter 对此方法做了优化,使重新执行变的很快,所以你可以重新构建任何需要更新的东西,而无需分别去修改各个 widget。
int _counter = 0;void _incrementCounter() {setState(() {_counter++;});}
二是定一个build函数,用于构建UI页面
@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:'),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),),);}
当MyHomePage第一次创建时,_MyHomePageState类会被创建,当初始化完成后,Flutter框架会调用 widget 的build方法来构建 widget 树,最终将 widget 树渲染到设备屏幕上。具体解读如下:
Scaffold是 Material 库中提供的页面脚手架,它提供了默认的导航栏、标题和包含主屏幕 widget 树(后同“组件树”或“部件树”)的body属性,组件树可以很复杂。本书后面示例中,路由默认都是通过Scaffold创建。body的组件树中包含了一个Center组件,Center可以将其子组件树对齐到屏幕中心。此例中,Center子组件是一个Column组件,Column的作用是将其所有子组件沿屏幕垂直方向依次排列; 此例中Column子组件是两个Text,第一个Text显示固定文本 “You have pushed the button this many times:”,第二个Text显示_counter状态的数值。floatingActionButton是页面右下角的带“+”的悬浮按钮,它的onPressed属性接受一个回调函数,代表它被点击后的处理器,本例中直接将_incrementCounter方法作为其处理函数。
完整流程如下:
当右下角的floatingActionButton按钮被点击之后,会调用_incrementCounter方法。在_incrementCounter方法中,首先会自增_counter计数器(状态),然后setState会通知 Flutter 框架状态发生变化,接着,Flutter 框架会调用build方法以新的状态重新构建UI,最终显示在设备屏幕上。
为什么把build放在State类中
1.方便状态访问
如果我们的StatefulWidget有很多状态,而每次状态改变都要调用build方法,由于状态是保存在 State 中的,如果build方法在StatefulWidget中,那么build方法和状态分别在两个类中,那么构建时读取状态将会很不方便。如果真的将build方法放在 StatefulWidget 中的话,由于构建用户界面过程需要依赖 State,所以build方法将必须加一个State参数,大概是下面这样:
Widget build(BuildContext context, State state){//state.counter...}
这样的话就只能将State的所有状态声明为公开的状态,这样才能在State类外部访问状态!但是,将状态设置为公开后,状态将不再具有私密性,这就会导致对状态的修改将会变的不可控。但如果将build()方法放在State中的话,构建过程不仅可以直接访问状态,而且也无需公开私有状态,这会非常方便。
2.方便继承StatefulWidget
例如,Flutter 中有一个动画 widget 的基类AnimatedWidget,它继承自StatefulWidget类。AnimatedWidget中引入了一个抽象方法build(BuildContext context),继承自AnimatedWidget的动画 widget 都要实现这个build方法。现在设想一下,如果StatefulWidget 类中已经有了一个build方法,正如上面所述,此时build方法需要接收一个 State 对象,这就意味着AnimatedWidget必须将自己的 State 对象(记为_animatedWidgetState)提供给其子类,因为子类需要在其build方法中调用父类的build方法
class MyAnimationWidget extends AnimatedWidget{@overrideWidget build(BuildContext context, State state){//由于子类要用到AnimatedWidget的状态对象_animatedWidgetState,//所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState//暴露给其子类 super.build(context, _animatedWidgetState)}
}
这样很显然是不合理的,因为
AnimatedWidget的状态对象是AnimatedWidget内部实现细节,不应该暴露给外部。- 如果要将父类状态暴露给子类,那么必须得有一种传递机制,而做这一套传递机制是无意义的,因为父子类之间状态的传递和子类本身逻辑是无关的。
总结来说
build方法需要根据可变状态(如_counter) 来构建用户界面。这些可变状态保存在State子类(如_MyHomePageState)中。将build方法放在State子类里,可以直接访问(通过context)这些状态(例如_counter),无需通过复杂的传递机制或将状态设置为公开,从而破坏了状态的封装性。StatefulWidget是对外的,负责接收不可变的配置数据。State是对内的,负责管理可变的状态。
参考
《Flutter实战·第二版》
https://juejin.cn/post/7033933629403168799
4.拓展阅读
flutter专栏–移动开发技术的发展与flutter的概要
flutter专栏–dart基础知识
关注我,有空一起闲聊

