Flutter小记

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(),
);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容