1.简介
1.1
- 2015,Flutter(当时叫Sky) 在Dart开发者峰会上亮相
- 2018-6,Flutter发布了首个预览版本
- 2018-12,Flutter1.0发布
- 2019-9,Flutter1.9发布, 添加Web端支持
- 2020-9,Flutter1.22发布,带来了对iOS14和Android11的支持
1.2 flutter下载
- flutter.cn,自行下载安装
PUB_HOSTED_URL dart的路径
FLUTTER_STORAGE_BASE_URL https://storage.flutter-io.cn
C:\yrvh\flutter_windows_3.10.5-stable\flutter\packages\flutter_tools\gradle\flutter.gradle
// google()
maven { url 'https://maven.aliyun.com/repository/google'}
maven { url 'https://maven.aliyun.com/repository/jcenter'}
maven { url 'http://maven.aliyun.com/nexus/content/groups/public'}
1.3 AndroidStudio下载
https://developer.android.google.cn/studio?hl=zh-cn
- Android Studio\jbr\bin 设置path环境变量
- C:\yrvh\flutter_windows_3.10.5-stable\flutter\bin 配置环境变量
1.4 创建项目
- 命令行创建 flutter create name123
- 编辑工具创建
- main.dart是文件入口
- AndroidStudio安装flutter和dart插件
1.5 目录介绍
文件夹:
.dart_tool
.idea
android
ios
lib
test
文件:
learn_flutter.iml 记录了某些配置
.gitignore
.metadata 对flutter的版本来做一个记录
.packages
.pubspec.yaml
.pubspec.lock
2.
2.1 入口函数main
(1) main(List<String> args){} dart的main函数可以接收一个参数, 但是flutter实际上是没有传过来这样一个参数的. 所以可以省略掉
(2) main() {
runApp(Widget); // 首先需要调用这个函数, 它是flutter框架给我们提供的全局函数
// 调用这个函数之前需要导入一个库, flutter/material.dart, runApp(Widget) 里边传入一个widget, flutter里万物皆可Widget
}
(3) 但是Widget 是个抽象类,不能实例化, 所以只能用它子类的对象. 多态,父类的引用 引用子类
main() {
runApp(Text("测试")); // 这样也会报错,因为不知从左网游排版还是从右向左排版
runAppp(Text("你好", textDirection: TextDirection.ltr); // 这次可以正常显示
}
2.2 Material设计风格
material是什么呢? (与其相对的是cupertino风格 这个是苹果风格)
material是Google公司推行的一套设计风格, 或者叫设计规范等.
里面有非常多的设计规范,比如颜色 文字的排版 响应动画与过度 填充等等.
在Flutter中高度集成了Material风格的Widget;
在我们的应用中,我们可以直接使用这些Widget来创建我们的应用.
MaterialApp(),有home属性,
main() {
runApp(
MaterialApp(
home: Center(
child: Text(
"你好啊",
//textDirection: TextDirection.ltr
// Material默认就是从左往右,所以这里可以省略
// Material默认给Text加了下划线
)
)
)
);
}
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false, // 去掉右上角debug
// Scaffold这个东西叫脚手架, 脚手架最主要一个作用帮我们搭建页面
// Scaffold有两个重要属性appBar和body
home: Scaffold(
appBar: AppBar(
title: Text("标题")
),
body: Center(
child: Text(
"你好啊",
//textDirection: TextDirection.ltr
// Material默认就是从左往右,所以这里可以省略
// Material默认给Text加了下划线
)
)
)
)
);
}
2.3 Widget, StatelessWidget,StatefulWidget
(1)
有状态的Widget: StatefulWidget 在运行过程中有状态需要改变.
无状态的Widget: StatelessWidget 内容是确定的没有状态的改变,没有数据data的改变
StatelessWidget它的数据通常直接写死,或传入一些不可修改的数据.
放在StatelessWidget中的数据必须被定义为final.
(2) build方法
Flutter在拿到我们自己创建的StatelessWidget时,就会执行它的build方法;
我们需要在build方法中告诉Flutter,我们的Widget希望渲染什么元素,比如一个Text()
statelessWidget没办法主动去执行build方法,当我们使用的数据发生变化时,build方法会被重新执行.
(3) build方法什么情况下被执行
当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时);
当我们的父Widget(外层)发生改变时,子Widget会被重新构建;
如果我们Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时.
(4) 优化
void main() => runApp(TYMyApp());
class TYMyApp extends StatelessWidget {
// Widget 是抽象类,必须实现类里边的抽象方法
// @override
// Element createElement() {
// return null;
// }
// StatelessWidget 类里边也有抽象方法 build
@override build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// Scaffold这个东西叫脚手架, 脚手架最主要一个作用帮我们搭建页面
// Scaffold有两个重要属性appBar和body
home: Scaffold(
appBar: AppBar(
title: Text("标题")
),
body: TYHomeBody(),
)
);
}
}
class TYHomeBody extends StatelessWidget {
@override
build(BuildContext context) {
return Center(
child: Text(
"你好啊",
//textDirection: TextDirection.ltr
// Material默认就是从左往右,所以这里可以省略
// Material默认给Text加了下划线
)
);
}
}
(5) @immutable 这个注解主要的作用是说明当前这个类是不可变的. StatelessWidget这个类就是一个不可变的类. 一旦这个类不可变,里边定义的所有的成员变量必须是final
(6) 在Flutter的开发中,所有的Widget都不能定义状态. 因为Widget这个类上边用@immutable来修饰, 所以只要是一个Widget你都是不可变的.
(7) StatefulWidget也是不能把变化的成员变量定义在外层, 也就是StatefulWidget不能定义状态(状态就是可以变化的成员变量), 创建一个单独的类,这个类负责维护状态.
class TYContent extends StatefulWidget {
// 所以在这里我们创建一个,单独的状态类. 实例化StatefulWidget时 createState()会返回一个状态类, 实例化StatelessWidget()时 build会返回一个组件Widget.
@override
State<StatefulWidget> createState() {
return TYContentState();
}
}
class TYContentState extends State<TYContent> { // 创建的状态类
var flag = true; // State的子类可以定义装态, 也就是可以定义变化的成员变量
// 实现父类的抽象方法
@override
Widget build(BuildContext Context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: flag,
onChanged: (value){
setState(() {
this.flag = value!;
});
}
),
Text("同意协议")
]
)
);
}
}
2.4
12.打包
12.1 安卓打包
(1) 生成秘钥
keytool -genkey -v -keystore ./test.keystore -keyalg RSA -keysize 2048
-validity 10000 -alias test123
(2)
查看单个证书 keytool -printcert -v -file mydomain.crt
查看证书所有信息 keytool -list -v -keystore test.keystore
用别名查看所有信息 keytool -list -v -keystore test.keystore -alias test123
查看MD5 keytool -list -v -keystore test.keystore | findstr(或grep) "MD5"
(2) 口令 xxxxwww
(3) 在/android/key.properties
(4) 在/android/app/build.gradle中配置
12.2苹果打包
12.2.1 基础
- 基本信息和版本信息和Android一致
12.2.2 用户权限配置
- 在iOS中某些权限,需要用户允许,为了添加这些权限需要配置info.plist文件
- xcode打开项目下的ios文件夹
12.2.3 流程
(1)申请开发者账号, developer.apple.com
(2)登记套装ID(BundleID) Identifiers
(3)在AppStoreConnect中创建一个app
(4)新建证书
钥匙串, 证书助理, 从证书颁发机构请求证书,
在官网certificates下发布证书,
下载这个证书,
创建app的profiles文件
创建完成后下载这个profiles
在xcode中打包
(5)安装brew
(6)自己安装ruby, brew install ruby,不用苹果自带的ruby,不要卸载苹果自带的ruby,并改变环境变量
vim ~/.bash_profile
vim ~/.zshrc
(7)安装cocoapods
(8) pod install
12.2.4 配置相关的证书
- developer.apple.com/account/
- 下载和安装证书,电脑才具备发布程序的能力
- 创建appid, 配置发布者证书(iOS Distribution)
13.Getx
1.简介
- Getx 是flutter上的轻量且强大的解决方案: 高性能的状态管理,智能的依赖注入和便捷的路由管理.
- Getx有3个原则:
- 性能: Getx专注于性能和最小资源消耗.Getx打包后的apk占用大小和运行时的内存占用小.
- 效率: Getx的语法非常简捷,并保持了极高的性能,能极大缩短你的开发时长.
- 结构: Getx可以将界面,逻辑,依赖和路由完全解耦,用起来更清爽,逻辑更清晰,代码更容易维护.
- Getx并不臃肿,却很轻量. 如果你只使用状态管理,只有状态管理模块会被编译,其他没用到的东西都不会被编译到你的代码中. 它众多的功能都在独立的容器中,只有在使用后才会启动.
- Getx有一个庞大的生态系统,能够在Android,iOS,Web,Mac,Linux,Windows和你的服务器上用同样的代码运行.
- 通过GetServer可以在你的后端完全重用你在前端写的代码.
- 此外,通过Get CLI,无论是在服务器还是在前端,整个开发过程都可以完全自动化.
- 此外,为了进一步提高您的生产效率,我们还为您准备了一些插件.
- getx_template: 一键生成每个页面必须的文件夹,文件,模板代码等等.
- Getx Snippets: 输入少量字母,自动提示选择后,可生成常用的模板代码.
2.案例计数器_小试牛刀
(1)步骤一,使用GetMaterialApp替换MaterialApp, GetMaterialApp并不是修改后的MaterialApp,他只是一个预先配置的Widget,他的子组件是默认的MaterialApp. GetMaterialApp会创建路由,注入它们,注入翻译,注入你需要的一切路由导航.
如果你只用Get来进行状态管理或依赖管理,就没有必要使用GetMaterialApp. GetMaterialApp对于路由,snackbar,国际化,bottomSheet,对话框以及与路由相关的高级apis和没有上下文context 的情况下是必要的.
(2) 步骤二,
class Controller123 extends GetxController{
var count = 0.obs;
increment() => count++;
}
(3) 步骤三,创建你的界面,使用StatelessWidget节省一些内存, 使用了Get你可能不在需要StatefulWidget
class Home extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put实例化你的类, 使其对当下的所有路由可用
final Controller123 c = Get.put(Controller123());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Getx Demo"),
centerTitle: true,
),
body: Container(),
)
}
}
3.snackbar
- 页面头部提示框
4.dialog
- 弹出框
5.bottomsheet
- 底部操作弹窗
6.路由
6.1 普通路由
Get.to(NextPage()); // 导航到该页面
Get.back(); // 要关闭snackbars, dialogs,bottomsheets或任何你通常会用Navigator.pop(context)关闭的东西.
Get.off(NextPage()); // 进入当前页面,并取消 上一个页面的选项(用于闪屏页,登录页面等)
Get.offAll(NextPage()); // 进入当前页面 并 取消之前的所有路由.
Get.arguments; // 获取上一个页面传过来的参数, 上个页面使用arguments做key
6.2 命名路由
- 如果你喜欢用别名做路由导航,Get也支持
路由页面的定义:
void main() {
runApp(
GetMaterialApp(
initialRoute: '/',
defaultTransition: Transition.zoom,
getPages: [
GetPage(name: '/', page: () => MyHomePage()),
GetPage(name: '/second', page: () => Second()),
GetPage(name: '/login', page: () => Login(), transition: Transition.zoom)
]
)
)
}
-----------------
Get.toNamed("/NextScreen"); // 导航到该页面
Get.offNamed("/NextScreen"); // 导航到该页面,并删除前一个页面
Get.offAllNamed("/NextScreen"); // 导航到该页面,并删除前面的所有页面
Get.arguments // 获取上一个页面传过来的参数, 上个页面使用arguments做key
Get.put(Controller())
Get.find // 你可以让Get找到一个正在被其他页面使用的Controller,并将它返回来.
- 动态网页连接, Get提供高级动态URL,就像在Web上一样.
(1)
Get.offAlllNamed("/NextScreen?device=phone&id=55");
Get.parameters["name"] // 用这种方式来取值
(2)path参数
GetPage(
name: '/profile/:userid',
page: () => UserProfile(),
)
Get.toNamed("/profile/7979")
Get.parameters["userid"] // 也是用这种方式来取值
- 中间件
在路由跳转之前执行(生命周期函数)
routingCallback: (routing) => {
print(routing)
}
7.状态管理
7.1 介绍
-
目前,flutter中有几种状态管理.但是,他们中的大多数都涉及到使用changeNotifier来更新widget, 这对于中大型应用的性能来说是一个很糟糕的方法. 你可以在flutter的官方文档中查看到, ChangeNotifier应该使用1个或最多2个监听器,这使得它们实际上无法用于任何中等或大型应用.
-
Get并不是比任何其他状态管理器更好或更差, Get不是其他状态管理器的敌人,因为Get是一个微框架,而不仅仅是一个状态管理器,既可以单独使用,也可以与其他管理器结合使用.
-
Get有两个不同的状态管理器: 简单的状态管理器(GetBuilder) 和响应式状态管理器Getx
7.2 响应式状态管理器
- Getx将响应式编程变得非常简单
- 你不需要创建StreamControllers
- 你不需要为每个变量创建一个StreamBuilder
- 你不需要为每个状态创建一个类
- 你不需要为一个初始值创建一个Get.
(1)
var name = 'tom'.obs // 只需要在末尾加上.obs
Obx(()=>Text("${controller.name}")); // 而在UI中,当你想显示该值并在值变化时更新页面,只需要这样做
(2) 声明一个响应式变量的几种方式
第一种是Rxtype:
final name = RxString("tom");
final name = RxBool(false);
final name = RxInt(8);
final name = RxDouble(3.4);
final name = RxList<Int>([]);
final name = RxMap<String,int>({});
第二种是Rx<Type>
final name = Rx<String>("tom");
final name = Rx<Bool>(false);
final name = Rx<Int>(8);
final name = Rx<Double>(3.4);
final name = Rx<List<Int>>([]);
final name = Rx<Map<String,int>>({});
第三种更实用,只需要在末尾加上 .obs
final name = tom.obs;
final name = false.obs;
final name = 8.obs;
final name = 3.4.obs;
final name = <Int>[].obs;
final name = <String, Int>{}.obs;
以上3中取值的时候,要加.value, name.value
7.3 可以使用obs的地方
(1)
final name = "tom".obs
final age = 22.obs
(2)
class User {
User({String name, int age});
var name;
var age;
}
final user33 = User(name: 'tom', age: 20).obs;
user.update((user)=> {user.name = 'miles', user.age = 20}); // 更新
(3) List和它里面的对象一样,是完全可以观察的.这样一来,如果你在List中添加一个值,它会自动重建使用它的widget.
你也不需要在List中使用.value, 神奇的dart api 允许我们删除它.不幸的是,像String和int这样的原始类型不能被扩展,使得.value的使用是强制的, 但是如果你使用get和serter来处理这些类型, 这将不是一个问题.
// controller
final String title = "UserInfo".obs
final list = List<User>().obs
// view
Text(controller.title.value)
ListView.builder(
itemCount: controller.list.length // List不需要用.value
)
(4)
// model
class User() {
User({this.name = '', this.age=''});
String name;
int age;
}
7.4 Workers
- Workers可以协助你在事件发生时触发特定的回调.
(1) ever(count1, (aa) => print("$aa 被更改"))
(2) once(count1, (aa) => print("$aa 只在第一次变化时回调"))
(3) debounce(count1, (aa) => print("$aa 每当用户停止操作 1秒后回调"), time: Duration(seconds: 1)) // 防抖
(4) interval(count1, (aa) => print("$aa 忽略一秒内所有变化"), time: Duration(seconds: 1))
8. 控制器 Controller
9. 简单状态管理 GetBuilder
10. 依赖管理
- Get.put()
- Get.lazyPut()
- Get.putAsync()
- Get.create()