6.2 屏幕宽高
6.2.1 系统提供的方法
需要在有context容器中才能使用,也就是build方法中使用
Widget build(BuildContext context){
final size =MediaQuery.of(context).size;
final width =size.width;
final height =size.height;
}
6.2.2 使用起来最简单的方法
需要提前定义,要不然拿不到,方法报错
import 'dart:ui';
final width = window.physicalSize.width;
final height = window.physicalSize.height;
6.3去掉debug标签
import 'package:flutter/material.dart';
import './pages/tabs.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
);
}
}
6.4 PreferredSize
此控件不对其子控件施加任何约束,并且不以任何方式影响孩子的布局。
此控件对自定义AppBar.bottom和AppBar非常有用。
PreferredSize 可以改变 appBar 的高度
Scaffold(
// 通过 PreferredSize 来改变 appBar的高度
appBar: PreferredSize(
preferredSize: const Size.fromHeight(50),
child: AppBar(
),
),
)
6.5 保存页面状态
参考:https://blog.csdn.net/qq_14876133/article/details/125393405
AutomaticKeepAliveClientMixin缓存组件
AutomaticKeepAlive 的组件的主要作用是将列表项的根 RenderObject 的 keepAlive 按需自动标记 为 true 或 false。为了方便叙述,我们可以认为根 RenderObject 对应的组件就是列表项的根 Widget,代表整个列表项组件,同时我们将列表组件的 Viewport区域 + cacheExtent(预渲染区域)称为加载区域 :
- 当 keepAlive 标记为 false 时,如果列表项滑出加载区域时,列表组件将会被销毁。
- 当 keepAlive 标记为 true 时,当列表项滑出加载区域后,Viewport 会将列表组件缓存起来;当列表项进入加载区域时,Viewport 从先从缓存中查找是否已经缓存,如果有则直接复用,如果没有则重新创建列表项。
封装组件代码
import 'package:flutter/material.dart';
class KeepAliveWrapper extends StatefulWidget {
final bool keepAlive;
final Widget child;
const KeepAliveWrapper({Key? key, this.keepAlive = true, required this.child})
: super(key: key);
@override
State<StatefulWidget> createState() {
return _KeepAliveWrapperState();
}
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
@override
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
//状态发生变化时调用
if (oldWidget.keepAlive != widget.keepAlive) {
//更新KeepAlive状态
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
@override
bool get wantKeepAlive => widget.keepAlive;
}
在使用的时候,只需要把组件,放到 KeepAliveWrapper 的child中即可。
6.6 配置命名路由
routers.dart
import 'package:flutter/material.dart';
import '../pages/Tabs.dart';
import '../pages/search.dart';
import '../pages/first.dart';
Map routes = {
"/": (context) => const Tabs(),
"/s": (context, {arguments}) => Search(arguments: arguments),
"/f": (context) => const FirstPage(),
};
//固定写法
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route =
MaterialPageRoute(builder: (context) => pageContentBuilder(context));
return route;
}
}
};
main.dart
import 'package:flutter/material.dart';
import './tools/routers.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(primarySwatch: Colors.blue),
// home: const Tabs(),
initialRoute: "/",
onGenerateRoute: onGenerateRoute,
);
}
}
传值
import 'package:flutter/material.dart';
class Message extends StatefulWidget {
const Message({super.key});
@override
State<Message> createState() => _MessageState();
}
class _MessageState extends State<Message> {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/s",
arguments: {"title": "我是标题", "content": "我是内容"});
},
child: const Text("跳转搜索页面"))
],
),
);
}
}
接收值
import 'package:flutter/material.dart';
class Search extends StatefulWidget {
final Map arguments;
const Search({super.key, required this.arguments});
@override
State<Search> createState() => _SearchState();
}
class _SearchState extends State<Search> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.arguments["title"]),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(widget.arguments["content"]),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("返回上一页"))
],
),
);
}
}
6.7 fluttertoast
1、打开网站 https://pub.dev/
2、搜索 fluttertoast
3、在pubspec.yaml中,的 dependencies 中,配置 fluttertoast: ^8.0.9
4、导入包:import 'package:fluttertoast/fluttertoast.dart';
5、使用
Fluttertoast.showToast(
msg: "提示信息",
toastLength: Toast.LENGTH_SHORT, // 针对android平台
gravity: ToastGravity.CENTER, // 方位
timeInSecForIosWeb: 1, // 提示时间
backgroundColor: Colors.red, // 背景颜色
textColor: Colors.white, // 文字颜色
fontSize: 16.0 // 字号
);
6.8 定时器
引入包:
import 'dart:async';
var t = Timer.periodic(const Duration(seconds: 3), (timer) {
print("定时执行:" + DateTime.now().toString());
// 取消定时器
// timer.cancel;
});
// 组件销毁的时候,取消定时器
void dispose() {
super.dispose();
t.cancel();
}
6.9 key
key
是用来作为Widget
、Element
和SemanticsNode
的标示,仅仅用来更新widget->key
相同的小部件的状态。
主要用于组件的排序,例如listview中,拖动组件,横竖屏切换
当你想要跨widget
树保留状态时 , 应该使用key
.
一、LocalKey有三种类型,用作diff算法的核心所在,用Element和widget进行比较
- ValueKey 以一个数据作为Key。如:数字、字符
- ObjectKey 以Object对象作为Key
- UniqueKey 可以保证Key的唯一性!(一旦使用Uniquekey那么就不存在Element复用 了!)
二、GlobalKey
- 1、GlobalKey可以获取到对应的Widget的State对象!
需求:当我们页面内容很多时,而需要改变的内容只有很少的一部分且在树的底层的时候,我们如何去实现增量更新?
通常情况下有两种方式,
- 第一种是通过方法的回调,去实现数据更新,
- 第二种是通过GlobalKey,在StatelessWidget引用StatefulWidget。
//在statelessWidget中引用statefulWidget更新UI
class GlobalKeyDemo extends StatelessWidget{
// 1、 定义GlobalKey
final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('GlobalDemo'),
),
// 2、给子控件设置key
body: ChildPage(
key: _globalKey,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
// 3、可以通过 globalKey.currentState 来获取子组件的属性和方法
_globalKey.currentState.data = 'old' + _globalKey.currentState.count.toString();
_globalKey.currentState.count++;
_globalKey.currentState.setState(() { });
},
),
);
}
}
class ChildPage extends StatefulWidget{
ChildPage({Key key}):super(key:key);
@override
_ChildPageState createState() => _ChildPageState();
}
class _ChildPageState extends State<ChildPage>{
int count = 0;
String data = 'hello Flutter';
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Column(
children: <Widget>[
Text(count.toString()),
Text(data),
],
),
);
}
}
6.10 Matrix4
参考:https://blog.csdn.net/liu__520/article/details/83796784
- scale:缩放比例 transform: Matrix4.diagonal3Values(2, 10, 1),
- transform: 移动
- rotationZ:绕Z轴旋转
- rotationX:绕X轴旋转
- rotationY:绕Y轴旋转
- columns:设置一个新的矩阵
- compose:复合平移、旋转、缩放,形成新的状态
- copy:复制一个4*4的张量(矩阵)
- identity:恢复初始状态,也就是4*4的单位矩阵
- inverted:取相反的矩阵,就是反着来
- outer(合并)、skew(扭曲)、skewX(x轴扭曲)、skewY(y轴扭曲)、zero(置零矩阵)、fromList(将一个16位的一维数组转换成4*4的矩阵)
常用的如下:
6.10.1 缩放
Matrix4.diagonal3Values(1, 1, 1)
表示缩放的比例,分别沿x,y,z三个方向,x轴正向向右,y轴正向向下,z轴正向从屏幕朝上,正值表示正向,>1表示放大,小于1大于0表示缩小,负值表示反向。
6.10.2 移动
Matrix4.translationValues
表示平移的距离,分别沿x,y,z三个方向,
x轴正向向右,
y轴正向向下,
z轴正向从屏幕朝上,
正值表示正向移动,负值表示负向移动,
其中z轴移动在平面上无法看出小错
6.10.3 旋转
Matrix4.rotationZ(pi / 6)
绕着Z轴旋转,正向是顺时针,负向是逆时针,
正向也就是从x轴正向往y轴正向旋转
Matrix4.rotationX(pi / 6)
绕着X轴旋转,正向是顺时针,负向是逆时针,
正向也就是从y轴正向往z轴正向旋转
Matrix4.rotationY(pi / 6)
绕着Y轴旋转,正向是顺时针,负向是逆时针,
正向也就是从x轴正向往z轴正向旋转
6.11 photo_view
图片预览插件
photo_view: ^0.13.0
简单使用
Widget _buildPhotoView() {
_galleryItems = [
'assets/images/icon_avatar_staff.png',
'assets/images/icon_avatar_staff.png',
'assets/images/icon_avatar_staff.png'
];
return PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: AssetImage(_galleryItems[index]),
initialScale: PhotoViewComputedScale.contained * 0.9,
);
},
itemCount: _galleryItems.length,
backgroundDecoration: const BoxDecoration(color: Colors.white),
pageController: PageController(initialPage: _currentPageIndex),
onPageChanged: (i) {
_currentPageIndex = i;
_valueNotifier.notifyListeners();
},
);
}