env config

Flutter

定义

  • 是一款移动应用程序SDK,一份代码可以同时生成iOS和Android两个高性能、高保真的应用程序

目标

  • 使开发人员能够交付在不同平台上都感觉自然流畅的高性能应用程序

高性能

  • Dart支持JIT(即时编译)和AOT(动态解释)两种模式(静态编译的程序在执行前全部被翻译为机器码C/C++,动态解释执行的则是一句一句边运行边翻译JavaScript/Python)

  • Flutter使用自己的渲染引擎来绘制UI,布局数据等由Dart语言直接控制.不像RN通过JavaScriptCore在JavaScript和原生之间进行通信,在一些滑动和拖动的场景不需要与原生频繁的通信提升性能.

核心原则

  • Flutter包括一个现代的响应式框架、一个2D渲染引擎、现成的widget和开发工具。这些组件可以快速地设计、构建、测试和调试应用程序

特点

  • Flutter既不使用WebView,也不使用平台(Android、iOS等)的原生控件

  • Flutter使用自己的高性能渲染引擎来绘制Widget。保证在Android和iOS平台上UI的一致性,避免对原生控件依赖而带来的限制及高昂的维护成本

  • Flutter Widget采用现代响应式框架构建,当widget的状态发生变化时,widget会重新构建UI,Flutter会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改

一切皆为widget

  • Widget是Flutter应用程序用户界面的基本构建块。每个Widget都是用户界面一部分的不可变声明。 与其他将视图、控制器、布局和其他属性分离的框架不同,Flutter具有一致的统一对象模型:widget。

  • Widget根据布局形成一个层次结构。每个widget嵌入其中,并继承其父项的属性。没有单独的“应用程序”对象,相反,根widget扮演着这个角色。

  • 您可以通过告诉框架使用另一个widget替换层次结构中的widget来响应事件,例如用户交互,替换后框架会比较新的和旧的widget,并高效地更新用户界面。

Widget可以被定义为

  • 一个结构元素(如按钮或菜单)

  • 一个文本样式元素(如字体或颜色方案)

  • 布局的一个方面(如填充)

为什么用

  • 提高开发效率,同一份代码开发iOS和Android

  • 用更少的代码做更多的事情

  • 轻松迭代

  • 在应用程序运行时更改代码并重新加载(热重载)

  • 修复崩溃并继续从应用程序停止的地方进行调试

  • Flutter框架提供的丰富的Material Design和Cupertino的widget 实现定制设计,不受原生控件的限制

实践内容

  • 掌握基本的dart语言

  • 开发环境搭建

  • 项目结构设计

  • 尺寸与设计稿

  • 路由设计

  • 网络请求设计

  • 组件规划

  • 状态管理

  • 开发布局盒模型

  • 开发调试技巧

  • 打包发布

环境搭建

基础条件

  • mac 系统, xcode工具, brew软件管理, vscode

配置步骤

  • 下载最新的flutter的sdk下载稳定版本

  • 将flutter目录配置到环境变量

  • 找到 ~ 目录下的 .bash_profile环境变量文件( command + shift + . 显示隐藏文件)。执行命令 vim .bash_profile编辑文件。配置如下

1
2
3
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
export PATH=/Users/liuyongshun/Documents/flutter/bin:$PATH

 /Users/liuyongshun/Documents/flutter 是我自己电脑的flutter的位置。根据自己实际情况改变

  • 上面配置好以后,执行一下命令 source .bash_profile刷新

  • flutter --version 如果能出来版本怎配置成功

  • 执行flutter doctor 检测安装依赖,如果没有的,则需要安装(可以用brew)

  • 安装好各个依赖后,再次检查flutter doctor,通过后则可以创建项目了

  • 创建项目 flutter create yourAppName 生成一个初始的flutter项目

  • 开发时,需要对项目打包,以及对项目内的文件操作,所以要对项目目录设置开放权限。 sudo chmod -R 777 yourAppName 777可读,可写,可执行

  • 有些时候可能对安装fluter的目录进行授权(ios更新系统后遇到无权限问题,报错(OS Error: Permission denied, errno = 13))

  • 下载安装最新版xcode,配置Xcode命令行工具

1
2
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch

 当你使用最新版本的Xcode时,大多数情况下这是的正确路径。如果需要使用不同的版本,则需要特殊指定该路径

  • 项目导入xcode,flutter项目下ios目录下双击Runner.xcodeproj 自动在xcode打开

  • 打开一个模拟器open -a Simulator或者用如图位置打开

  • 运行项目 cd yourAppName 然后运行 flutter run

  • 一般情况不会在xcode上开发调试,太卡顿,也不好用。所以还需要vscode

  • vscode 安装插件flutter ,dark,Awesome Flutter Snippets三个插件,重启vscode。这时候可以关闭xcode。但是不要关闭模拟器。然后打开terminal,右下角能检测到你的模拟设备,如果提示没有找到flutter环境变量,可以手动配置。flutter run

  • 跑起来后可以连接你没有关闭的xcode 模拟器,开发调试时有如下命令

1
2
3
4
5
6
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

如果不会vim

在finder任意位置 – 右键标题 – 选择Macintosh HD – 用户 – 你的用户名 – 按快捷键 command + shift + . 显示隐藏文件 – 打开编辑并保存

PS:

如果上面的环境变量配置正常,但是flutter命令提示not find,建议重新下载sdk,或者换一个版本,可能存在下载问题

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── android              // android平台相关代码
├── ios // ios平台相关代码
├── images // 图片资源
├── test // 用于存放测试代码
├── pubspec.yaml // 配置文件,一般存放一些第三方库的依赖和图片到引入
└──lib // flutter开发主要代码
├── config
├── main.dart
├── model
├── pages
├── provider
├── routers
├── services
└── widget

尺寸适配

1、可以使用flutter_screenutil这个库

2、配置依赖

1
2
3
4
5
dependencies:
flutter:
sdk: flutter
# add flutter_ScreenUtil
flutter_screenutil: ^0.5.3

3、导入import 'package:flutter_screenutil/flutter_screenutil.dart';

4、配置下面代码放在build方法里面, 然后宽度高度和你的设计稿对应

1
ScreenUtil.instance = ScreenUtil(width: 750, height: 1334)..init(context);

5、这时候设置宽度高度的时候就可以用我们设计稿的宽度高度了

1
2
width: ScreenUtil.getInstance().setWidth(375),
height: ScreenUtil.getInstance().setHeight(200),

入口

每一个 flutter 项目的 lib 目录里面都有一个 main.dart 这个文件就是 flutter 的入口文件,main.dart 里面的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main(){ runApp(MyApp())}
也可以简写
void main()=>runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: new Text(
'Hello, 大是大非!',
textDirection: TextDirection.ltr,
),
);
}
}

其中的 main 方法是 dart 的入口方法。runApp 方法是 flutter 的入口方法。

MyApp 是自定义的一个组件,每定义一个类,就是一个组件 ,如果开发过react,该处非常好理解。

该runApp函数接受给定的Widget并使其成为widget树的根。

在编写应用程序时,通常会创建新的widget,这些widget是无状态的StatelessWidget或者是有状态的StatefulWidget, 具体的选择取决于您的widget是否需要管理一些状态。

widget的主要工作是实现一个build函数,用以构建自身。一个widget通常由一些较低级别widget组成。Flutter框架将依次构建这些widget,直到构建到最底层的子widget时,这些最低层的widget通常为RenderObject,它会计算并描述widget的几何形状。

flutter里所有和数字相关都是dobule类型,结尾要有小数,比如20 => 20.0

顶层组件

MaterialApp 封装了应用程序实现 Material Design 所需要的 一些 Widget。一般作为顶层 widget 使用。

Scaffold:Material Design 布局结构的基本实现。此类提供了用于显示 drawer、
snackbar 和底部 sheet 的 API。

  • appBar: 显示在界面顶部一个appBar

  • body: 显示当前界面主要内容

  • drawer: 抽屉菜单控件

对于web开发人员,开发转变对比

StatelessWidget/StatefulWidget

在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget

StatelessWidget 类比: react UI组件

Flutter中的StatelessWidget是一个不需要状态更改的widget - 它没有要管理的内部状态。

StatefulWidget 类比: react 容器组件

使用setState方法管理StatefulWidget的状态的改变。调用setState告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法

Container组件

Container自身尺寸的调节分两种情况:

  • Container在没有子节点(children)的时候,会试图去变得足够大。除非constraints是unbounded限制,在这种情况下,Container会试图去变得足够小。
  • 带子节点的Container,会根据子节点尺寸调节自身尺寸,但是Container构造器中如果包含了width、height以及constraints,则会按照构造器中的参数来进行尺寸的调节。

button 组件

宽度占满屏幕

在Button之外再套一层控制大小,直接使用FlatButton、RaisedButton、OutlineButton

1
2
3
4
5
6
7
8
9
10
SizedBox(
width: double.infinity,
height: 50,
child: RaisedButton(
onPressed: () {},
child: Text("宽度占满了"),
color: Colors.green,
textColor: Colors.white,
),
),

直接使用MaterialButton

1
2
3
4
5
6
MaterialButton(
onPressed: () {},
child: Text("宽度占满了"),
minWidth: double.infinity,
height: 50.0,
)

设置在ButtonTheme中设置宽度

1
2
3
4
5
6
7
8
9
10
11
12
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
buttonTheme: ButtonThemeData(minWidth: double.infinity, height: 50.0),
),
home: SplashPage(),
);
}
}

图片组件

Image.asset() 引入本地图片

  • 首先在根目录下建立imgs文件夹

  • 其次在pubspec.yaml的assets下引入(flutter默认注释掉了)

1
2
3
assets:
- imgs/2.0x/icon.jpeg
- imgs/3.0x/icon.jpeg
  • 最后Image.asset('imgs/2.0x/icon.jpg')

  • 如果没有显示不妨试试重启

Image.network(‘www.baidu.com/ff.jpg') 引入远程图片

列表和动态列表

普通列表

ListTile 这个组件必须在这是一个material组件,所以外层必须使用material包裹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Mine extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('我的')),
body: ListView(
padding: EdgeInsets.all(10),
children: <Widget>[
ListTile(
leading: Icon(Icons.home),
title: Text('我的订单'),
)
],
)
);
}
}

自定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class IconContainer extends StatelessWidget{
double size;
IconData icon;
Color color;

IconContainer(this.icon,{this.size,this.color=Colors.blue}){
this.size=32.0;
}

@override
Widget build(BuildContext context) {
return Container(
width: this.size+60,
height: this.size+60,
color:this.color,
child: Center(child: Icon(this.icon,color:Colors.white,size:this.size))
);
}
}

路由,命名路由,跳转,数据传递

基本路由跳转

当前页面: PageA.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import '../Search.dart';

class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("跳转到搜索页面"),
onPressed: () {
//路由跳转 =====
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>SearchPage()
)
);
//路由跳转 =====
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary
);
}
}

基本路由传值

跳转页PageA

1
2
3
4
5
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>FormPage(name: '表单')
)
);

目的页 FormPage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class FormPage extends StatelessWidget {
// 定义title 数据类型
String titl;
// 接受参数 下面写法是title为可选参数。默认是表单
FormPage({this.title="表单"});
// 接受参数 下面写法是必传数据
FormPage(this.title);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Text('body')
);
}
}

返回上一页

发生跳转后,flutter回自动给头部加上返回按钮,但是有些时候我们需要手动触发返回。可以用如下方法

1
Navigator.of(context).pop();

https://www.jianshu.com/p/b2ba2382e824

https://juejin.im/post/5d550862f265da03b31bc789

https://api.flutter.dev/

返回
顶部