0.关于AppBar中leading
如果没有leading,automaticallyImplyLeading为true,就会默认返回箭头
如果 没有leading 且为false,空间留给title
如果有leading,automaticallyImplyLeading 这个参数就无效了
1.关于ElevatedButton
image.png
ElevatedButton(
child: Text("立即体验"),
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(Color(0xFF535149)),
backgroundColor: MaterialStateProperty.all(ColorFit.mainColor),
textStyle: MaterialStateProperty.all(TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold
)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)
)),
side: MaterialStateProperty.all(BorderSide(color: Colors.red, width: 2))),
onPressed: () { },
)
2.关于initState中拿不到context
解决:
Future.delayed(Duration.zero, () {
// 延迟0秒,异步执行
Navigator.of(context).pushReplacementNamed(MainPage.routeName);
});
3.关于PageView使用切换图片时,图片之间会有短暂的背景显示
解决:
// Implicit:含蓄的
设置属性 allowImplicitScrolling: true
4.关于取消scroller边缘弹簧效果
解决:
设置属性 physics: ClampingScrollPhysics()
5.关于设置命名路由问题
描述:设置命名路由时,主路由设置成 '/' 路由A,其他所注册的路由命名为 '/xxx'(非'/'),则系统会默认初始路由设置成由 '/'命名的路由A,这样在使用push替换路由(pushReplacementNamed)时,导航栏上会显示出返回按钮(如果此路由有导航栏),或左侧边缘可以拉回到主路由A页面。
【明明在MaterialApp中设置的initialRoute:为引导页/Launch页,但系统还是会默认初始路由为 '/'的主路由。】
解决:
可能原因注册其他路由时,其他路由的命名都为 / 开头;
例如像 引导页/Launch页 路由命名时,前面不用加 / 这样就正常了。
建议:不是经常使用到的页面,则路由命名可以不添加 /
6.关于全局消除水波纹效果,长按水波纹效果
解决:
theme: ThemeData(
splashColor: Colors.transparent, // 全局设置点击水波纹颜色, 去除长按不放, 水波效果
highlightColor: Colors.transparent // 全局去除点击的水波纹效果
)
7. TabBar() + TabBarView() + TabController配合
TabBar:设置标题,设置指示器样式等
TabBarView:存放标题对应的页面
TabController:使得 TabBar 与 TabBarView进行关联
选择栏.png
7. NestedScrollView 与 CustomScrollView
NestedScrollView:用来处理复杂情况下的滑动应用场景,如向上滑动视图时,要折叠隐藏一部分内容,这时候就需要使用到 NestedScrollView 与 SliverAppBar 的结合使用。
CustomScrollView:用来处理更为复杂的布局,可以将SliverAppBar,SliverList和SliverGrid SliverPadding SliverToBoxAdapter SliverPersistentHeader, SliverFillRemaining,SliverFillViewport组合等来使用。
8. 关于TextField 获取焦点 + 取消焦点
①、创建FocusNode实例aNode,并设置对应TextField的属性focusNode: aNode
②、获取焦点:FocusScope.of(context).requestFocus(aNode);
③、失去焦点:aNode.unfocus();
9. 回收键盘操作
SystemChannels.textInput.invokeMethod("textInput.hide");
10. 关于键盘监听操作
1、使用 with WidgetsBindingObserver 绑定监听
2、由于键盘开启后,应用视图尺寸会改变,则会被 WidgetsBindingObserver 监听到, 就会回调走此函数
@override
void didChangeMetrics() {}
@override
void didChangeMetrics() {
super.didChangeMetrics();
// 当键盘开启后, 界面会往上, 这里绑定监听界面缩小
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// 获取底部被遮挡的高度
double keybordHeight = MediaQuery.of(context).viewInsets.bottom;
print("键盘高度: $keybordHeight");
// 判断
if (keybordHeight == 0) { // 键盘收起状态
} else {
}
});
}
11. 关于去处导航栏左侧自带返回按钮
appBar: AppBar(
automaticallyImplyLeading: false,
),
12. 关于dart中math函数
int i = -1; //定义一个整型变量i
double j = 2.1; //定义一个小数j
print(i.abs());//求i的绝对值,打印 ‘1’
print(j.ceil());//求j的向上最大整数,打印'3'
print(j.floor());//求j的向下的最大整数,打印'2'
print(j.round());//求离j最近的整数,四舍五入,打印'2'
print(j.truncate());//截取掉小数点取整,打印'2'
double j = 2.6;//验证小数位超过0.5的情形
print(j.round());//求离j最近的整数,四舍五入,打印'3'
print(j.truncate());//截取掉小数点取整,打印'2'
print(j.clamp(1,3));//如果j再1-3之间则返回 j,这里打印j的值 ‘2.6’;否则返回离其最近的边界值。
print(j.clamp(3,4));//打印‘3’,原理同上
print(j.clamp(1,2));//打印‘2’,原理同上
//保留n 位小数: .toStringAsFixed(n)
double j = 2.6;
String rs = j.toStringAsFixed(2) = "2.60";
1.toStringAsFixed(3); // 1.000
(4321.12345678).toStringAsFixed(3); // 4321.123
(4321.12345678).toStringAsFixed(5); // 4321.12346
123456789012345.toStringAsFixed(3); // 123456789012345.000
10000000000000000.toStringAsFixed(4); // 10000000000000000.0000
5.25.toStringAsFixed(0); // 5
13. 关于flutter中List既能获取value,也能获取下角标index
例子:final List<String> _titles = ['2021', '2020', '2019', '2018', '2017', '2016'];
...
tabs: titles.asMap().entries.map((item) {
return Tab(
child: Container(
child: Text('$(item.value) === $item.key'),
),
);
}).toList()
14. 关于flutter中拦截导航栏返回事件
class SearchPage extends StatelessWidget {
static const String routeName = '/search';
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
print('true:可点击返回上一个界面;false:点击返回效果被禁用');
return false;
},
child: Scaffold(
appBar: AppBar(
title: Text(''),
),
body: Center(
child: Text(''),
),
),
);
}
}
15. 关于flutter中关于导航栏左侧宽度不够问题
实现如下图:
image.png
AppBar(
elevation: 1.0,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Image.asset(ImageTool.loadOthersImgPath('other_back_dark')),
Text(widget.navTitle, style: TextStyle(
fontSize: 14,
color: Colors.black
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
)
],
),
),
16. 关于flutter中使用 shared_preferences 出现 Unhandled Exception: Null check operator used on a null value 问题
解决:
void main() {
WidgetsFlutterBinding.ensureInitialized();
}
17. 关于flutter中查询 文本中共有多少个换行,并替换换行符为空格
/// 换行符个数:
final numLines = '\n'.allMatches(content).length + 1;
/// \n 替换成 空格" "
String rsContent = model.content.replaceAll(RegExp(r"\n"), " ");
18. 关于监听一个界面的 pop / push (WidgetsBindingObserver)
image.png
代码:
/* 用于监听 每个界面的 push pop 组件出现或者消失的回调,主要是要靠路由的监听 */
import 'package:flutter/material.dart';
class AppRouteObserver {
//这是实际上的路由监听器
static final RouteObserver<ModalRoute<void>> _routeObserver = RouteObserver<ModalRoute<void>>();
//这是个单例
static AppRouteObserver get shared => AppRouteObserver();
static AppRouteObserver? _instance;
factory AppRouteObserver() {
return _instance ??= AppRouteObserver._internal();
}
AppRouteObserver._internal() {}
RouteObserver<ModalRoute<void>> get routeObserver => _routeObserver;
}
使用:
⑴
。。。 with WidgetsBindingObserver
⑵
@override
void initState() {
WidgetsBinding.instance?.addObserver(this);
super.initState();
}
⑶
@override
void didChangeDependencies() {
super.didChangeDependencies();
//路由订阅
AppRouteObserver.shared.routeObserver
.subscribe(this, ModalRoute.of(context)!);
}
⑷
@override
void didPush() {
//当前页面显示时
print("哈哈didPush");
super.didPush();
}
@override
void didPushNext() {
//当前页面 push 到另一个页面
print("哈哈didPushNext");
super.didPushNext();
}
@override
void didPop() {
//当前的页面被pop.
print("哈哈didPop");
super.didPop();
}
@override
void didPopNext() {
//另一个页面 pop 到当前页面时.
print("哈哈didPopNext");
super.didPopNext();
}
⑸
@override
void dispose() {
AppRouteObserver.shared.routeObserver.unsubscribe(this);
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
19. 关于flutter中ListTile、ExpansionTile 设置 leading 和 title之的间隔;
利用 Transform 属性 transform: Matrix4.translationValues(-16, 0, 0),
ListTile(
leading: TicketImages.loadImage(
"confirm_alipay",
size: const Size(24, 24),
),
title: Transform(
transform: Matrix4.translationValues(-16, 0, 0),
child: Text(
TicketStrings.pay_ali,
style: TicketTextStyle.boldTextStyle(16, Colors.black),
),
),
trailing: TicketImages.loadImage(
"ticket_select_city_select",
size: const Size(24, 24),
),
)
20、关于 GetX 中路由跳转解释
off: 进入下一个页面,但没有返回上一个页面的选项(A off B to C,C 点击返回会到 A )
offAll: 进入下一个页面并取消之前的所有路由
until: 反复返回,直到表达式返回真
offUntil: 转到下一条路由,并删除所有之前的路由,直到表达式返回true
offNamed: 导航到下一个页面并删除前一个页面
21、关于 GetX 相关知识点
// 给出当前页面的args。
Get.arguments
//给出以前的路由名称
Get.previousRoute
// 给出要访问的原始路由,例如,rawRoute.isFirst()
Get.rawRoute
// 允许从GetObserver访问Rounting API。
Get.routing
// 检查 snackbar 是否打开
Get.isSnackbarOpen
// 检查 dialog 是否打开
Get.isDialogOpen
// 检查 bottomsheet 是否打开
Get.isBottomSheetOpen
// 删除一个路由。
Get.removeRoute()
//反复返回,直到表达式返回真。
Get.until()
// 转到下一条路由,并删除所有之前的路由,直到表达式返回true。
Get.offUntil()
// 转到下一个命名的路由,并删除所有之前的路由,直到表达式返回true。
Get.offNamedUntil()
//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb
// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width
// 提供当前上下文。
Get.context
// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay
// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。
// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height
// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()
/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()
/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()
/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()
/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()
/// 类似于 MediaQuery.of(context).orientation;
context.orientation()
///检查设备是否处于横向模式
context.isLandscape()
///检查设备是否处于纵向模式。
context.isPortrait()
///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()
///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()
///查询设备最短边。
context.mediaQueryShortestSide()
///如果宽度大于800,则为真。
context.showNavbar()
///如果最短边小于600p,则为真。
context.isPhone()
///如果最短边大于600p,则为真。
context.isSmallTablet()
///如果最短边大于720p,则为真。
context.isLargeTablet()
///如果当前设备是平板电脑,则为真
context.isTablet()
///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()
22、关于定位权限请求
/// 权限处理类(暂不需要)
class MainPermission {
// 判断定位是否有权限
static Future<bool> checkPermission() async {
PermissionStatus status = await Permission.location.status;
if (status == PermissionStatus.granted) {
// 权限通过
return true;
} else {
// 发起定位请求:第一次申请 || 权限永久拒绝 || 权限拒绝等...
status = await Permission.location.request();
if (status == PermissionStatus.granted) {
return true;
} else {
return false;
}
}
}
}
23、延迟操作
/// 用于延迟 定时器
Timer? _timer;
// 延时操作搜索
void delayOperation(Function? doSomething) {
if (_timer != null) {
_timer?.cancel();
}
_timer = Timer(const Duration(milliseconds: 350), () {
doSomething?.call();
_timer = null;
});
}
24. clipBehavior参数枚举说明
none:不裁剪
hardEdge:裁剪但不应用抗锯齿,裁剪速度比none模式慢一点,但比其他方式快。
antiAlias:裁剪而且抗锯齿,以实现更平滑的外观。裁剪速度比antiAliasWithSaveLayer快,比hardEdge慢。
antiAliasWithSaveLayer:带有抗锯齿的剪辑,并在剪辑之后立即保存saveLayer
25. dart 中的深拷贝
// 站点列表(进行深拷贝)目前没找到 dart 的 List 深拷贝方式
List<ConfirmSaleTicketStationModel> stattionList = arguments['List_ConfirmSaleTicketStationModel'];
// 深拷贝站点列表
routeStationList = List<ConfirmSaleTicketStationModel>.from(
stattionList.map((e) {
// 拷贝
var model = ConfirmSaleTicketStationModel.fromMap(e.toMap());
// 特殊类型不能拷贝的,手动拷贝(例如 枚举)
model.type = e.type;
model.textColor = e.textColor;
return model;
}).toList(),
);
25、flutter 中获取文字的size
/// value: 文本内容;fontSize : 文字的大小;fontWeight:文字权重;maxWidth:文本框的最大宽度;
/// maxLines:文本支持最大多少行 ;
/// locale:当前手机语言;textScaleFactor:手机系统可以设置字体大小(默认1.0)
static Size ai_calculateTextHeight(
String value,
TextStyle style,
double maxWidth,
int maxLines,
) {
TextPainter painter = TextPainter(
///AUTO:华为手机如果不指定locale的时候,该方法算出来的文字高度是比系统计算偏小的。
locale: WidgetsBinding.instance.window.locale,
maxLines: maxLines,
textDirection: TextDirection.ltr,
textScaleFactor: 1, //字体缩放大小
text: TextSpan(
text: value,
style: style,
),
ellipsis: maxLines == 1 ? '...' : null,
);
painter.layout(maxWidth: maxWidth);
/// 文字的宽度: painter.width
return painter.size;
}
26、关于简单形状设置
使用ShapeDecoration可以做出各种形状:
斜切角:BeveledRectangleBorder
圆角: RoundedRectangleBorder
超椭圆: SuperellipseShape
体育场: StadiumBorder
圆形: CircleBorder
示例:
//斜切角形状示例
Container(
width: 120,
height: 120,
decoration: ShapeDecoration(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(16)
),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage('。。。')
)
)
)
27、关于flutter textField设定高度后,文字无法居中
如果给textField设定的布局高度小于它的默认高度,那么它的居中就会有问题.
解决:
border 不能直接使用InputBorder.none
改为:decoration: InputDecoration(border: OutlineInputBorder(borderSide: BorderSide.none),),
上面设置完后文字偏上用TextAlignVertical.bottom修正
TextField(textAlignVertical: TextAlignVertical.bottom,);
可以使用
TextField(
decoration: InputDecoration(isCollapsed: true,)
)
配合给定高度 + 设置 alignment: Alignment.center
28、ConstrainedBox的解释
// 对子组件添加额外的约束,有时候子组件需要自动调整宽度和高度
ConstrainedBox(
.expand:宽度高度是可选参数,在不传入时依赖于父组件,占用父组件剩余空间,有传入,则设定多大就是多大
.loose:最大宽度和最大高度 由传入的size 决定,未超出能紧则紧
constraints: BoxConstraints.expand(),
);