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

flutter专栏--深入剖析你的第一个flutter应用

使用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之前,你需要配置你的开发环境。具体的步骤官网做了较为详细的概括,这里将不再赘述,主要分为几个步骤。

  1. 下载flutter sdk, vscode配置flutter相关插件
  2. 下载配置android studio,下载jdk
  3. 下载配置xcode
  4. 测试手机开启授权

创建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

可以配置FlutterDart版本

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)。

  1. Stateful widget 可以拥有状态,这些状态在 widget 生命周期中是可以变的,而 Stateless widget 是不可变的。
  2. 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基础知识

关注我,有空一起闲聊

在这里插入图片描述

http://www.xdnf.cn/news/20047.html

相关文章:

  • 从一次Crash分析Chromium/360浏览器的悬空指针检测机制:raw_ref与BackupRefPtr揭秘
  • 留学第一天,语言不通怎么办?同声传译工具推荐来了
  • 常用假设检验方法及 Python 实现
  • 亚马逊云代理商:配置安全组规则步骤
  • kafka Partition(分区)详解
  • nestjs 阿里云服务端签名
  • 深度学习篇---SGD+Momentum优化器
  • Photoshop - Photoshop 触控手势
  • 电表连网不用跑现场!耐达讯自动化RS485转Profinet网关 远程配置+技术支持,真能做到!
  • ASP.NET 实战:用 SqlCommand 打造一个安全的用户注册功能
  • SIC8833芯片智能充气泵设计方案
  • 原创未发表!POD-PINN本征正交分解结合物理信息神经网络多变量回归预测模型,Matlab实现
  • 第二家公司虽然用PowerBI ,可能更适合用以前的QuickBI
  • pip completion工具作用(生成命令行自动补全脚本)(与pip-bash-completion区别)
  • 东土智建 | 让塔吊更聪明的“四大绝技”工地安全效率双升级
  • EasyMeeting-注册登录
  • PDF-XChange Editor:全功能PDF阅读和编辑软件
  • 《华为基本法》——企业文化的精髓,你学习了几条?
  • 技术实战:从零开发一个淘宝商品实时数据采集接口
  • 《嵌入式硬件(一):裸机概念与80c51单片机基础》
  • Docker 运行 PolarDB-for-PostgreSQL 的命令,并已包含数据持久化配置
  • Scrapy框架实战:大规模爬取华为应用市场应用详情数据
  • 实现 TypeScript 内置工具类型(源码解析与实现)
  • C语言中的运算符
  • 自动化运维-ansible中的条件判断
  • 前端框架(Vue/React):界面更新的运行链路
  • mysy2使用
  • CC攻击的主要来源
  • 鸿蒙Next图形绘制指南:从基础几何图形到复杂UI设计
  • vue3 vite 自适应方案