初始化项目
项目目录
UI 库 material
import 'package:flutter/material.dart';
Material 是一种标准的移动端和 Web 端的 UI 框架,是一套 Google 的设计规范,Flutter 项目以Material 为 UI 基础。
App 结构
-
MaterialApp
-
Scaffold
- appBar(应用头部)
- body(应用主体)
- floatingActionButton(浮动按钮)
- drawer (左侧抽屉菜单)
- endDrawer (右侧抽屉菜单)
基础组件
Text
设置自定义字体
-
导入字体文件
字体文件可以网上找到,例如:https://fonts.google.com/
选择需要的字体文件,然后下载
将字体文件放到 Flutter 项目目录中 -
在 pubspec.yaml 中声明字体
flutter: #...... fonts: # family 属性决定了字体的名称,你将会在 TextStyle 的 fontFamily 属性中用到。 - family: SourceSansPro fonts: - asset: fonts/Source_Sans_Pro/SourceSansPro-Black.ttf - asset: fonts/Source_Sans_Pro/SourceSansPro-BlackItalic.ttf # weight 属性指定了文件中字体轮廓的字重为 100 的整数倍 weight: 300 # style 属性指定文件中字体的轮廓是否为 italic 或 normal style: italic
lcon
Icon 用来声明图标,Icons 是Flutter 中的图标组件
Icon(
Icons.ac_unit_rounded,
color:Colors.green,
size:40,
),
在线预览:https://material.io/resources/icons
Color
- Color(自定义颜色)
- Flutter 中通过 ARGB 来声明颜色
- const Color(0xFF42A5F5); // 16进制的ARGB = 透明度 + 六位十六进制颜色
- const Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5);
- const Color.fromARGB(255, 66, 165, 245);
- const Color.fromRGBO(66, 165, 245, 1.0); // O = Opacity
- Colors(英文字母声明的颜色)
- Colors.red
布局 - Container
- child (声明子组件)
- padding (margin)
- EdgeInsets (all(), fromLTRB(), only())
- decoration 是 container 的修饰器,用来设置背景和边框
- BoxDecoration (边框,圆角,渐变,阴影,背景色,背景图片)
- alignment 内容对齐方式
- Alignment (内容对齐)
- transform
- Matrix4 (平移 - translate、旋转 - rotate、缩放 - scale、斜切 - skew)
线性布局
- Column
- Column 中的主轴方向是垂直方向
- mainAxisAlignment:MainAxisAlignment 主轴对齐方式
- crossAxisAlignment:CrossAxisAlignment 交叉抽对齐方式
- children:内容
- Row
- Row 中的主轴方向是水平方向(其他属性与 Column 一致)
弹性布局
- Flex
- direction (声明主轴方向)
- mainAxisAlignment (声明主轴对齐方式)
- textDirection (声明水平方向的排列顺序)
- crossAxisAlignment (声明交叉轴的对齐方式)
- verticalDirection (声明垂直方向的排列顺序)
- children (声明子组件)
- Expanded (可伸缩组件)
- flex (声明弹性布局所占比例)
- child (声明子组件)
流式布局
- Wrap (解决内容溢出问题)
- spacing (主轴方向子组件的间距)
- alignment (主轴方向的对齐方式)
- runSpacing (纵轴方向子组件的间距)
- runAlignment (纵轴方向的对齐方式)
- Chip (标签)
- CircleAvatar (圆形头像)
层叠布局
Card
Card(
margin: EdgeInsets.all(30),
color: Colors.purpleAccent[100],
shadowColor: Colors.yellow, // 阴影颜色
elevation: 20, // 阴影高度
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40),
side: BorderSide(
color: Colors.yellow,
width: 3,
),
),
child: Column(
children: [
ListTile(
leading: Icon(
Icons.supervised_user_circle_rounded,
size: 50
),
title: Text(
"张三",
style: TextStyle(
fontSize: 30
),
),
subtitle: Text(
"董事长",
style: TextStyle(
fontSize: 20
),
),
),
Divider(),
ListTile(
title: Text(
"电话:13333333333",
style: TextStyle(
fontSize: 20
),
),
),
ListTile(
title: Text(
"地址:XXXXXXXX",
style: TextStyle(
fontSize: 20
),
),
)
],
)
),
按钮
图片
Image.asset(
'images/bg1.jpg',
width: 200,
height: 200,
fit: BoxFit.fill,
),
// 加载网络图片
Image.network(
'http://cdn.cnbj1.fds.api.mi-img.com/mi-mall/5a260090e0e08770b0bd865845a4b4ab.jpg',
repeat: ImageRepeat.repeat,
colorBlendMode: BlendMode.colorDodge,
color: Colors.green,
),
列表
- SingleChildScrollView (类似于Android中的ScrollView)
- child (子组件)
- padding (内边距)
- scrollDirection (滚动方向: Axis.horizontal | Axis.vertical)
- reverse (初始滚动位置,false 头部, true 尾部)
- physics ( 结束拖动时效果响应)
- ClampingScrollPhysics:Android 下微光效果
- BouncingScrollPhysics:iOS 下弹性效果
- ListView
- 加载列表的组件(加载所有的 Widgets,适用 widgrt 较少的场景)
- ListTile (leading、title、subtitle、trailing、selected)
- ListView.builder
- 按需加载 widget ,性能比默认构造函数高,适用 widget 较多的场景
- ListView.separated
- 比 ListView.builder 多了分隔器
- 加载列表的组件(加载所有的 Widgets,适用 widgrt 较少的场景)
- GridView (网格布局)
- children (子组件)
- scrollDirection (滚动方向)
- gridDelegate
- SliverGridDelegateWithFixedCrossAxisCount (指定列数 - 子组件宽度自适应)
- SliverGridDelegateWithMaxCrossAxisExtent (指定子组件宽度 - 列数自适应)
- GridView.count (列数固定)
- GridView.extend (子组件宽度固定)
- GridView.buildre (动态网格布局)
- ScrollPhysics physics (确定可滚动控件的物理特性)
- BouncingScrollPhysics (允许超出边界 - 反弹效果)
- ClampingScrollPhysics (防止超出边界 - 夹住效果)
- AlwaysScrollableScrollPhysics (始终响应滚动)
- NeverScrollableScrollPhysics (不响应滚动)
其他组件
Cupertino
- Material
- 安卓风格的组件
- import ‘package:flutter/material.dart’;
- Cupertino
- ios 风格的组件
- import ‘package:flutter/cupertino.dart’;
- https://flutter.dev/docs/development/ui/widgets/cupertino
SafeArea
- SafeArea 主要用来解决刘海屏的问题。
第三方库组件
dio
- dio 是一个强大的 Dart Http 请求库 (类似 axios)
- 使用步骤
- 在 pubsepc.yaml 中添加 dio 依赖
- 安装依赖 (pub get | flutter packages get | VS Code 中保存配置,自动下载)
- 引入 import ‘package:dio/dio.dart’;
- 使用:https://pub.dev/packages/dio/example
flutter_swiper
- Flutter 中最好的轮播组件,适配,Android 和 iOS
- 使用步骤
- 在 pubsepc.yaml 中添加 flutter_swiper 依赖
- 安装依赖 (pub get | flutter packages get | VS Code 中保存配置,自动下载)
- 引入 import ‘package:flutter_swiper/flutter_swiper.dart’;
- 使用
shared_preferences
- shared_preferences 是一个本地数据缓存库 (类似 AsyncStorage)
- 使用步骤
- 在 pubsepc.yaml 中添加 shared_preferences 依赖
- 安装依赖 (pub get | flutter packages get | VS Code 中保存配置,自动下载)
- 引入 import ‘package:shared_preferences/shared_preferences.dart’;
- 使用
操作
- 增
- setString(key, value)
- 删
- remove(key) | clear()
- 改
- 更改就是重新设置数据
- setString(key, value)
- 查
- getString(key)
状态管理
StatefulWidget
- Flutter 中的组件,按状态划分
- StatelessWidget (无状态组件)
- StatefulWidget (有状态组件)
- 按状态作用域划分
- 组件内私有状态 (StatefulWidget)
- 跨组件状态共享 (lnheritedWidget、Provider)
- 全局状态 (Redux | fish-redux 、mobx …)
- 状态组件的组成
- StatefulWidget (组件本身不可变 - @immutable)
- State (将变化的状态放到 State 中维护)
DataTable
InheritedWidget
- What:提供了沿树向下,共享数据的功能
- 即子组件可以获取父组件(InheritedWidget 的子类)的数据
- Why
- 依赖构造函数传递数据的方式不能满足业务需求
- 所以,需要一个新的,更好的跨组件数据传输方案
- How
- BuildContext.dependOnInheritedWidgetOfExactType()
生命周期
- initState() 组件对象插入到元素树中时
- didChangeDependencies() 当前状态对象的依赖改变时
- build() 组件渲染时
- setState() 组件对象的内部状态变更时
- didUpdateWidget() 组件配置更新时
- deactivate() 组件对象在元素中暂时移除时
- dispose() 组件对象在元素树中永远移除时
Provider
- Provider 是对 InheritedWidget 的封装
- 优点
- 简化资源的分配与处理
- 懒加载
- Provider 的实现原理
- 使用
路由与导航
路由简介
- Route
- 一个路由是一个屏幕或页面的抽象
- Navigator
- 管理路由的组件。Navigator可以通过路由入栈和出栈来实现页面之间的跳转
- 常用属性
- initialRoute: 初始路由,即默认页面
- onGenerateRoute: 动态路由(根据规则匹配动态路由)
- onUnknownRoute: 未知路由,也就是 404
- routers:路由集合
匿名路由
- Navigator
- push(跳转到指定组件)
Navigator.push( context, MaterialPageRoute(builder: (context) => 组件名称()) );
- pop(回退)
Navigator.pop(context)
- push(跳转到指定组件)
命名路由
MaterialApp(
// ... // 其他配置
routes: { // 注册路由 (路由表)
'home': (context) => Home(),
'product': (context) => Product(),
'productDetail': (context) => ProductDetail(),
},
initialRoute: 'home', // 初始路由页面
onUnknownRoute: (RouteSettings setting) => MaterialPageRoute(
builder: (context) => UnknowPage() // 未知路由
),
);
动态路由
MaterialApp(
onGenerateRoute: (RouteSettings setting) {
// 匹配首页 /
print('当前路径:'+setting.name);
if (setting.name == '/') {
return MaterialPageRoute(builder: (context) => Home());
}
if (setting.name == '/product') {
return MaterialPageRoute(builder: (context) => Product());
}
// 匹配 /product/:id
var uri = Uri.parse(setting.name);
print(uri.pathSegments);
if (uri.pathSegments.length == 2 && uri.pathSegments.first == 'product') {
var id = uri.pathSegments[1];
return MaterialPageRoute(builder: (context) => ProductDetail(id: id));
}
// 最后跳到未知路由
return MaterialPageRoute(builder: (context) => UnknowPage());
},
)
路由传参
- 匿名路由
- 命名路由
Drawer 导航
BottomNavigationBar 底部导航菜单
TabBar - 选项卡菜单
表单
Switch
- 常用属性
- CupertinoSwitch (iOS 风格的开关)
- import ‘package:flutter/cupertino.dart’;
Checkbox
- 常用属性
- CheckboxListTile
- title (标题)
- subtitle (子标题)
Radio
- 常用属性
- RadioListTitle (单选列表)
TextField (表单)
日历
- CalendarDatePicker (日历选择器)
- initialCalendarMode
- DatePickerMode.day
- DatePickerMode.year
- initialCalendarMode
- showDatePicker (日期选择器)
- initialDatePickerMode (year | day)
- initialEntryMode (calendar | input)
- showTimePicker (时间选择器)
Form
动画
动画分类
- 补间(Tween)动画
- 在补间动画中,我们定义开始点和结束点、时间线以及定义转换时间和速度曲线。然后由系统计算,从开始点运动到结束点。从而形成动画效果。
- 例如:透明度从 0 到 1,颜色值从 0 到 255
- 拟物动画
- 拟物动画是对真实世界的行为进行建模,使动画效果类似于现实中的物理效果。
- 例如:弹簧,阻尼,重力,抛物线等。
Animation
- Animation,是 Flutter 动画库中的一个核心类。它包含动画的值和状态两个属性,定义了动画的一系列监听方法。
- 监听值:
- addListener
- removeListener
- 监听状态
- addStatusListener
- removeStatusListener
- 监听值:
动画状态
- AnimationStatus.dismissed
- 动画初始状态
- AnimationStatus.completed
- 动画结束状态
- AnimationStatus.forward
- 动画从开始状态执行到结束状态
- AnimationStatus.reverse
- 动画反向执行,从结束状态执行到开始状态
@override
void initState() {
// TODO: implement initState
super.initState();
// 1.创建 AnimationController
controller = AnimationController(
duration: Duration(milliseconds: 400),
vsync: this
);
// 2.1 声明动画曲线
animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
// 2.2 设置动画值的范围
animation = Tween(begin: 50.0, end: 400.0).animate(controller);
// 3. 监听动画
animation.addListener(() {
print(animation.value);
setState(() {
});
});
// 4. 执行动画
// controller.forward();
}
多语言(国际化)
组件国际化
-
在 pubspec.yaml 中添加依赖
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter
-
配置 MaterialApp
-
localizationsDelegates
指定哪些组件需要进行国际化(例如:Material、Widgets、Cupertino)当前,设置具体内容之前,先引入 flutter_localizations import
‘package:flutter_localizations/flutter_localizations.dart’; -
supportedLocales
指定要支持哪些语言
import 'package:flutter_localizations/flutter_localizations.dart'; MaterialApp( ... localizationsDelegates: [ // 本地化代理 CustomLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: [ const Locale('en', 'US'), // 美国英语 const Locale('zh', 'CN'), // 简体中文 ], )
-
文本国际化
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
class CustomLocalizations {
final Locale locale;
CustomLocalizations(this.locale);
// static Map<String, Map<String, String>> _localizedValues = {
// "en": {
// "title": "Home",
// "greet": "Hello",
// },
// "zh": {
// "title": "首页",
// "greet": "你好",
// },
// };
Map<String, String> _localizedValues;
Future<bool> loadJSON() async {
String jsonString = await rootBundle.loadString('lang/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedValues = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
String t(String key) {
// _localizedValues['zh']['title']
// return _localizedValues[locale.languageCode][key];
return _localizedValues[key];
}
static CustomLocalizations of(BuildContext context) {
return Localizations.of(context, CustomLocalizations);
}
static CustomLocalizationsDelegate delegate = CustomLocalizationsDelegate();
}
class CustomLocalizationsDelegate extends LocalizationsDelegate<CustomLocalizations> {
@override
bool isSupported(Locale locale) {
return ["en", "zh"].contains(locale.languageCode);
}
@override
Future<CustomLocalizations> load(Locale locale) async {
// return SynchronousFuture(CustomLocalizations(locale));
CustomLocalizations localizations = CustomLocalizations(locale);
await localizations.loadJSON();
return localizations;
}
@override
bool shouldReload(covariant LocalizationsDelegate<CustomLocalizations> old) {
return false;
}
}
import 'CustomLocalizations.dart';
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Text(
// "Hello",
// Localizations.of(context, CustomLocalizations).t('greet'),
CustomLocalizations.of(context).t('greet'),
style: TextStyle(
fontSize: 60
)
),
);
}
}
多主题
MaterialApp(
theme: ThemeData(
// fontFamily: 'SourceSansPro',
primaryColor: Colors.red,
accentColor: Colors.yellow,
// 针对旧按钮有效
buttonTheme: ButtonThemeData(
textTheme: ButtonTextTheme.accent,
splashColor: Colors.green,
height: 50
),
textTheme: TextTheme(
subtitle1: TextStyle(
fontSize: 30,
color: Colors.green,
)
),
iconTheme: IconThemeData(
color: Colors.pink,
size: 40
),
cardTheme: CardTheme(
color: Colors.brown[100],
shape: Border.all(width: 10, color: Colors.red),
elevation: 20,
)
),
// 适配终端的主题风格
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
theme: CustomTheme.lightTheme,
darkTheme: CustomTheme.darkTheme,
)