创建的flutter项目
组件Material Design
Flutter中无状态组件(StatelessWidget)和有状态组件
App结构内容
点击查看代码
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget{
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: 'Flutter Demo',
home:Home(),
);
}
}
class Home extends StatelessWidget{
const Home({super.key});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('首页'),
leading:Icon(Icons.menu) ,
actions: [
Icon(Icons.settings)
],
elevation: 0.0,
centerTitle: true,
),
body: HelloFlutter(),
);
}
}
class HelloFlutter extends StatelessWidget{
const HelloFlutter({super.key});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child: Center(
child: Text(
'hello flutter',
textDirection: TextDirection.ltr
)
)
);
}
}
点击查看代码
Column(
children: [
Text(
"Flutter 是谷歌开发的一款开源、免费的,基于 Dart 语言的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生应用。",
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 30,
color: Colors.red,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.italic,
decoration: TextDecoration.lineThrough,
decorationColor: Colors.blue,
// fontFamily: 'SourceSansPro',
),
textAlign: TextAlign.left,
maxLines: 3,
overflow: TextOverflow.ellipsis,
textScaleFactor: 1.5,
),
RichText(
text: TextSpan(
text: "Hello",
style: TextStyle(
fontSize: 40,
color: Color.fromARGB(255, 0, 0, 255)
),
children: [
TextSpan(
text: "Flutter",
style: TextStyle(
fontSize: 40,
color: Color.fromRGBO(255, 0, 0, 0.5)
),
),
TextSpan(
text: "你好世界",
style: TextStyle(
fontSize: 30,
color: Color.fromARGB(0xFF, 0x00, 0xFF, 0x00)
),
)
]
),
)
],
)
<application
android:usesCleartextTraffic="true">
</application>
Button组件
TextButton(文本按钮)、ElevatedButton(凸起按钮)、OutlinedButton(轮廓按钮)
ButtonStyle()函数用于改变按钮样式,示例如下,其中边框用side,阴影用elevation,边框形状用shape,可给按钮全局设置主题
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
TextStyle(
fontSize: 30
)
),
foregroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮时的前景色
return Colors.red;
}
// 默认状态的颜色
return Colors.blue;
}),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
// 按下按钮时的前景色
return Colors.yellow;
}
// 默认状态的颜色
return Colors.white;
}),
shadowColor: MaterialStateProperty.all(Colors.yellow),
elevation: MaterialStateProperty.all(20),
side: MaterialStateProperty.all(
BorderSide(
color: Colors.green,
width: 2,
)
),
// 声明按钮形状
shape: MaterialStateProperty.all(
StadiumBorder(
side: BorderSide(
color: Colors.green,
width: 2,
)
)
),
// 设置按钮大小
minimumSize: MaterialStateProperty.all(Size(200, 100)),
// 设置水波纹的颜色
overlayColor: MaterialStateProperty.all(Colors.purple),
)
图片组件加载本地图片需要在pubspec.yaml的flutter部分添加图片配置
列表组件
1.ingleChildScrollView
scrollDirection: Axis.horizontal,//设置水平方向
padding: EdgeInsets.all(10),//设置内外编剧
//利用list组件的generate方法批量生成元素
children: List.generate(
100,
(index) => OutlinedButton(
onPressed: () {},
child: Text('按钮$index')
),
)
2.ListView
默认构造函数和命名构造函数来生成list
常用ListView.builder()函数来生成列表
2.GridView网格布局
一种是指定子元素的列数,另一种是子元素的默认宽度
Scroll的组件的physics属性(确定可滚动控价的物理特性)
material.dart 提供了 Android 风格的组件;而 Cupertino 提供了 iOS 风格的组件。
判断是安卓环境还是IOS环境
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'dart:io';
if (Platform.isIOS) {
// 加载 iOS 风格的组件
} else if (Platform.isAndroid) {
// 加载 Android 风格的组件
}
第三方组件
dio
发送请求代码
void getIpAddress() async {
try {
final url = "https://httpbin.org/ip";
Response response = await Dio().get(url);
String ip = response.data['origin'];
print(ip);
} catch (e) {
print(e);
}
}
}
flutter_swiper
shared_preferences是一个本地数据缓存库,类似AsyncStorage
增删改查操作
大概率使用在状态组件上
状态管理
StatefulWidget(状态组件)
状态组件的基本代码
class MyState extends StatefulWidget {
@override
_MyStateState createState() => _MyStateState();//继承的抽象类,抽象方法必须实现
}
class _MyStateState extends State<MyState> {
int _num = 0;
void _increment() {
setState(() {
_num++;
});
}
void _decrement() {
setState(() {
_num--;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: _decrement,
child: Text('-')
),
Padding(
padding: EdgeInsets.all(20),
child: Text('$_num')
),
ElevatedButton(
onPressed: _increment,
child: Icon(Icons.add)
)
],
)
);
}
}
DataTable表格组件是一个有状态的组件,通过setState((){})来更改状态,例如让表格通过DataColumn中的onSort方法来进行排序
DataColumn(
label: Text('年龄'),
numeric: true,
onSort: (int columnIndex, bool asscending) {
setState(() {
_sortAscending = asscending;
if (asscending) {
data.sort((a, b) => a.age.compareTo(b.age));
} else {
data.sort((a, b) => b.age.compareTo(a.age));
}
});
}
),
数据中的字段可通过一个类进行声明
class User {
String name;
int age;
bool selected;
User(this.name, this.age, {this.selected = false});
}
通过写一个方法来获取表格所有行数据
List<User> data = [
User('张三', 18),
User('张三丰', 218, selected: true),
User('张翠山', 30),
User('张无忌', 60),
];
List _getUserRows() {
List<DataRow> dataRows = [];
for (int i = 0; i < data.length; i++) {
dataRows.add(
DataRow(
selected: data[i].selected,
onSelectChanged: (selected) {
setState(() {
data[i].selected = selected;
});
},
cells: [
DataCell(Text('${data[i].name}')),
DataCell(Text('${data[i].age}')),
DataCell(Text('男')),
DataCell(Text('---')),
]
)
);
}
return dataRows;
}
生命周期
无状态组件只有build,无所谓生命周期
didUpdateWidget是例如组件主题更换时触发的生命周期函数
didChangeDependencies是当组件A和B都依赖于某数据时,组件B改变了这一数据,组件A就会触发这一生命周期函数,使用数据共享组件InheritedWidget
组件就是Dart类,可通过构造函数的方式,进行跨组件的状态参数传递
InheritedWidget生命的状态数据可以在需要使用状态的InheritedWidget后代组件中,访问InheritedWidget状态数据
class MyCounter extends StatefulWidget {
MyCounter({Key key}) : super(key: key);
@override
_MyCounterState createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
@override
Widget build(BuildContext context) {
// 使用 InheritedWidget 中的共享数据
return Text(ShareDataWidget.of(context).num.toString());
}
}
// 数据共享组件
class ShareDataWidget extends InheritedWidget {
final int num;
final Widget child;
ShareDataWidget({Key key, this.child, @required this.num}) : super(key: key, child: child);
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
@override
bool updateShouldNotify(ShareDataWidget oldWidget) {
return true;
}
}
第三方组价Provider
观察者跟watch类似,跨组件的状态管理,放在根组件全局
使用
路由与导航
匿名路由
onPressed: () {
return Navigator.push(
context,
MaterialPageRoute(builder: (context) => Product())
);
},
onPressed: () => Navigator.pop(context),
命名路由
命名路由的跳转onPressed: () => Navigator.pushNamed(context, 'product')
动态路由
命名路由和动态路由互斥
在组件中通过构造函数获取到传来的id,并且路由跳转的时候pushName方法的第二个参数加上'/'
命名路由传参
Drawer导航就是抽屉菜单,通过左滑或者右滑可以显示,在Scaffold中进行配置
BottomNavigationBar
定义状态组件,声明菜单和页面,Scaffold中的body动态,在Scaffold中声明bottomNavigationBar组件
Tab导航
form组件
Switch Checkbox Radio TextField 日历,时间选择器,Form
创建表单首先创建表单唯一键,唯一键中有验证方法,提交表单,和重置方法等
动画
补间动画
定义开始点到结束点、时间线,由系统计算开始点到结束点形成动画效果。例如透明度从0到1
拟物动画
对真实世界的行为进行建模,例如弹簧,重力,抛物线等
Animation(动画对象) 值(具体属性字段,例如透明度从0到1)和动画状态(开始、执行期间,执行完成)
AnimationController(动画控制器)抽象类,含有动画执行时间,执行最大小值,控制动画的方法等,最重要的是vsync(作用防止动画页面切换到后台时消耗不必要的资源)包含Ticker对象,获取到Ticker对象是获取每一帧刷新的通知
Tweens(插值器,补间动画)职责是定义从输入范围到输出范围的映射
Curves (常用的动画曲线即动画的执行速度)
动画步骤,初始化一个动画控制器,有时间,有vsync,有value(可以不指定值),第二步指定动画的曲线和取值范围是对value的约束,执行动画把动画渲染到组件上,渲染进程是vsync中的Ticker发起的,Ticker向组件发通知让组件一帧一帧执行动画效果
class _AnimationDemoState extends State<AnimationDemo> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
@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();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('放大')
),
ElevatedButton(
onPressed: () {
controller.reverse();
},
child: Text('缩小')
),
ElevatedButton(
onPressed: () {
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 反向执行动画
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
// 正向执行动画
controller.forward();
}
});
controller.forward();
},
child: Text('重复')
),
ElevatedButton(
onPressed: () {
controller.stop();
},
child: Text('停止')
),
Icon(
Icons.favorite,
color: Colors.red,
size: animation.value
),
Opacity(
opacity: controller.value,
child: Text('Hello World')
)
],
),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
交织动画
交织动画需要给每段动画设置时间间隔(在curve处使用Interval)
Transform.translate()平移 Transform函数对组件进行矩阵变换
点击查看代码
class _AnimationDemoState extends State<AnimationDemo> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
Animation sizeAnimation;
Animation colorAnimation;
Animation rotationAnimation;
@override
void initState() {
// TODO: implement initState
super.initState();
// 1. 创建 AnimationController
controller = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
// 2. 创建动画
animation = CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.5)
)..addListener(() {
setState(() {
});
});
// 3. 让动画反复运行
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 反向执行动画
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
// 正向执行动画
controller.forward();
}
});
// 4. 设置其他动画
sizeAnimation = Tween(begin: 0.0, end: 200.0).animate(animation);
colorAnimation = ColorTween(begin: Colors.yellow, end: Colors.red)
.animate(CurvedAnimation(
parent: controller,
curve: Interval(0.5, 0.8, curve: Curves.bounceIn)
))
..addListener(() {
setState(() {
});
});
rotationAnimation = Tween(begin: 0.0, end: 2*pi).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.8, 1.0, curve: Curves.easeIn)
)
);
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('重复')
),
ElevatedButton(
onPressed: () {
controller.stop();
},
child: Text('停止')
),
Opacity(
opacity: controller.value,
child: Transform.rotate(
angle: rotationAnimation.value,
child: Container(
width: sizeAnimation.value,
height: sizeAnimation.value,
color: colorAnimation.value
)
)
)
],
),
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
Hero动画
Hero动画来实现跨页面的动画效果
在页面A定义起始Hero组件,声明tag
在页面B定义目标Hero组件,绑定相同的tag
应用场景,点击图片全屏显示
国际化
组件国际化,文本国际化