flutter快速上手
本文旨在介绍flutter项目的大致流程,与基础语法。
一、安装配置
官方文档:安装和环境配置
扩展插件:
- Dart
- Flutter
- Flutter Widget Snippets 快捷操作
- Flutter Intl 语言国际化
遇到的问题:
- SDK无法下载,项目运行卡住,下载依赖卡住,timeout等
- 以上80%都是网络导致的
解决方案:
方式一:终端启用代理
- 科学上网
mac设置代理
export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=socks5://127.0.0.1:7890
方式二:抓包或查看日志文件,配host
- 查看日志(这里使用任意抓包工具都可)
- 查询ip,配置host
IP查询:IP查询
方式三:配置镜像
- 具体方法查询各类搜索引擎
二、Dart语言基础
推荐一个在线运行dart的平台:DartPad
- main函数是项目入口函数,不可或缺。(有点类似java的)
void main() async {
// // 声明变量
// // 通用var
// var v1 = 12; // 此时v1变量固定为int类型
// v1 = "str"; // 报错
// // 定义整型
// int? i1 = 23; // ? 可空
// // 定义double
// double d1 = 3.14;
// // 定义字符串
// String s1 = "str22";
// // 定义数组
// List<int> iList = [2,3,1,2];
// // 定义map
// Map<String,int> sMap = {"s1":23,"s2":34};
// print(sMap["s1"]); // 不能使用sMap.s1调用
// // dynamic与object 声明变量
// // dynamic相当于不声明变量的类型,后期可以重新赋值为不同的类型
// dynamic d1 = "str";
// d1 = 12;
// // 所有类型都是Object的子类,因此可以重新赋值为不同的类型
// Object o1 ={"xx":343};
// o1 = 12;
// // 两者的区别
// print(d1["xx"]); // 编译时不会报错
// print(o1["xx"]); // 编译时会报错,只能使用 Object 的属性与方法,
// print(o1.toString()); // 只能使用 Object 的属性与方法,
// // final和const
// // final声明的变量只能赋值一次,首次声明可以赋值也可以不赋值
// late final f1 ; // 第一次不赋值,最好在前面加上late关键字
// f1 = "str1";
// f1 = "str2"; // 报错,不能第二次赋值
// // const 声明时就必须赋值
// const c1 = "ccc"; // 报错,必须赋值
// const c2; // 报错,必须赋值
// // 函数声明
// bool isExist(String arg1,{required double arg2,int? arg3, bool arg4 = false}){
// return false;
// }
// // 调用
// isExist("arg1",arg2:2.2,arg3:12);
// // class 类
// // Person1
// Person1("name",age:13); //abstract抽象类 不能直接实例化
// Student s2 = Student('sss',28);
// s2.walk();
// // 异步,Future类似js中的promise
// Future.delayed(const Duration(seconds: 2),(){
// return "hi word!";
// }).then((data){
// print(data);
// }).catchError((err){
// print(err);
// }).whenComplete((){});
// String data = await Future.delayed(const Duration(seconds: 2),(){
// return "hi world!";
// });
// print(data);
// // Future.wait
// var start = DateTime.now().millisecondsSinceEpoch;
// await Future.wait([
// Future.delayed(const Duration(seconds: 3)),
// Future.delayed(const Duration(seconds: 3)),
// Future.delayed(const Duration(seconds: 3)),
// ]);
// var end = DateTime.now().millisecondsSinceEpoch;
// print('执行时间:${end - start}');
}
// // abstract抽象类,只能被继承,不能直接实例化
// abstract class Person1 {
// late final name;
// late final age;
// // 构造方法
// Person1(this.name,{required this.age});
// }
// mixin Eat {
// eat() {
// print('eat');
// }
// }
// mixin Walk {
// walk() {
// print('walk');
// }
// }
// // 继承,mixin类似多继承
// class Student extends Person1 with Eat, Walk{
// late final name;
// late final age;
// // 构造方法
// Student(this.name,this.age):super(name,age:age);
// }
三、项目搭建
创建项目
方式一:命令行创建
flutter create 项目名字
方式二:android studio创建
项目结构
运行与调试
选择设备
- 模拟器设备会自动识别,(安卓可通过android studio创建模拟器,ios需要使用xcode创建)
- 真机设备需要使用开发者模式
运行
# 运行,默认是debug模式
flutter run
# release运行
flutter run --release
# profile模式运行(只能在真机运行)
flutter run --profile
- 在终端,使用
r
热重载,相当于重新执行builder
,不会执行main
和initState
(initState
是有状态组件的某个生命周期) - 在终端,使用
R
热重启,重新运行项目,但不会下载新引入的依赖,若新增依赖,建议关闭运行后重启 DEBUG
模式下才能执行热重载或者热重启- iOS真机运行,建议使用
flutter run --release
,debug
模式运行时,退出app
重新进入app
会崩溃
调试
调试运行后:
- 在vscode中,点击vscode左侧的运行与调试,点击
create a launch.json
,选择dart&flutter
- 使用web调试页面:完成上一步后,打开:http://localhost:9100/,输入控制台打印的地址,即可调试
使用此方式进行调试,每次点击保存都会热重载,不需要输入r
。个人比较推荐
四、Flutter项目是如何运行
运行默认的计数器项目:
- 每次点击+,屏幕上的数字就会加1
lib/main源码:
- Flutter中几乎所有的元素都是一个
widget
,widget
可以理解为组件,控件。包括:UI组件,手势监听控件,主题组件等 - flutter通过widget来构建UI和事件处理,
widget
中还可以嵌套widget
// 导入了 Material UI 组件库,这是官方写好的库,里面包含了很多实用的widget
import 'package:flutter/material.dart';
// 程序入口
void main() {
// 运行app,runApp接收一个widge参数
runApp(const MyApp());
}
// StatelessWidget是无状态组件,不管理状态
class MyApp extends StatelessWidget {
const MyApp({super.key});
// 通过build方法构建 UI 界面
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo', // app标题
theme: ThemeData( // app主题
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
// 使用Material3的样式主题等
useMaterial3: true,
),
// 应用首页,MyHomePage
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
// StatefulWidget有状态组件,需要状态管理就需要这个组件,比如这里的_counter就是我们需要管理的状态
class MyHomePage extends StatefulWidget {
// 构造函数接收一个title参数
const MyHomePage({super.key, required this.title});
final String title;
// 有状态组件需要创建一个State类
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
// setState更改状态,运行setState会重新执行build方法
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// 使用StatefulWidget中的数据,使用widget获取
title: Text(widget.title),
),
body: Center( // Center让它的子元素居中
child: Column(// Column组件让子元素垂直布局,类似css中的弹性布局,垂直方向。如果需要水平布局可以使用Row
mainAxisAlignment: MainAxisAlignment.center, // 主轴方向居中
children: <Widget>[
// const表示该组件是不变的,重新build时不会重新build该组件
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter', // 引用状态
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
// floatingActionButton是浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, // 点击时执行_incrementCounter方法
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
分析运行流程:
我们找到浮动按钮的代码:
- 点击浮动按钮,执行
_incrementCounter
方法
// floatingActionButton是浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, // 点击时执行_incrementCounter方法
tooltip: 'Increment',
child: const Icon(Icons.add),
),
_incrementCounter
方法的代码:
setState
更改状态,运行setState
会重新执行build
方法,重绘页面(类似react的setState)
void _incrementCounter() {
setState(() {
_counter++;
});
}
_counter
发生改变重新运行build
方法,页面发生变化
Text(
'$_counter', // 此时的_counter就是最新的_counter
style: Theme.of(context).textTheme.headlineMedium,
),
五、打包上线
ios与android打包都需要配置相关的证书,这里不做细讲,主要分享相关命令与shell
脚本
iOS打包:
主要介绍两种包:
adhoc
:测试包,只有指定的设备才能下载使用,设备数量限制100。
appstore
,这种是正式包,上架到App Store商店需要这种包。
打包命令
- 打包为
ipa
文件 ExportOptions.plist
文件中指定了打哪种包,如何获取ExportOptions.plist
文件?可以先使用Xcode
界面打包,生成的文件中就有ExportOptions.plist
,直接使用即可。--dart-define
指定项目中的环境变量。在flutter项目中可以访问到,可用于区别后端的环境接口
flutter build ipa --release --export-options-plist=$PWD/ios/ExportOptions.plist --dart-define=APP_ENV=dev
Android打包
主要介绍两种包:
apk
包:大部分应用商店都使用这个包。且android手机可以直接安装这个包。aab
包:上线谷歌商店,需要打这种包
打包命令
--dart-define
指定项目中的变量,一般用于环境变量配置
// 打包为aab包
flutter build appbundle --dart-define=APP_ENV=dev
// 打包为apk包
flutter build apk --dart-define=APP_ENV=dev
shell脚本
- 项目中每次打包要执行多条命令,这样是否麻烦,编写shell脚本简化打包
- 注:window电脑需要在git bash中运行shell脚本
bash build.sh // 执行
build.sh
#!/bin/bash
# 定义菜单选项
env_options=("测试环境" "生产环境" "开发环境")
platform_options=("Android" "iOS" "All")
env_mode="test"
ios_mode="adhoc"
apk_mode="apk"
# 是否上线
read -p "是否需要上线(默认n)[y/n]: " is_release
# 不上线的包
select_platform(){
echo -e ""
# 选择平台
PS3="请选择平台(默认为Android): "
select choice in "${platform_options[@]}"; do
case $REPLY in
1)
echo -e "\n平台:Android 环境:${env_mode} 是否上线:${is_release} 包类型:${apk_mode}\n"
echo -e "开始打包: flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
break
;;
2)
echo -e "\n平台:iOS 环境:${env_mode} 是否上线:${is_release} 包类型${ios_mode}\n"
echo -e "开始打包:flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode} "
flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}
break
;;
3)
echo -e "\n平台:iOS,Android 环境:${env_mode} 是否上线:${is_release} 包类型:${apk_mode},${ios_mode}\n"
echo -e "开始打包Android:flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
echo -e "开始打包iOS:flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}"
flutter build ipa --release --export-options-plist=$PWD/ios/${ios_mode}.plist --dart-define=APP_ENV=${env_mode}
break
;;
*)
echo -e "\n平台:Android 环境:${env_mode} 是否上线:${is_release} 包类型:${apk_mode}\n"
echo -e "开始打包:flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}"
# flutter build ${apk_mode} --dart-define=APP_ENV=${env_mode}
break
;;
esac
done
}
# 选择环境
select_env(){
PS3="请选择环境(默认为测试环境): "
select choice in "${env_options[@]}"; do
case $REPLY in
1)
env_mode="test"
break
;;
2)
env_mode="pro"
break
;;
3)
env_mode="dev"
break
;;
*)
env_mode="test"
break
;;
esac
done
}
if [ "${is_release}" = "y" ]
then
# 要上线
env_mode="pro"
ios_mode="appStore"
apk_mode="appbundle"
select_platform
else
echo -e ""
is_release="n"
select_env
select_platform
fi
效果:
转载自:https://juejin.cn/post/7284069963123736635