Flutter 日常问题汇总

225 阅读4分钟

我正在参加「掘金·启航计划」

以下问题属于个人开发中遇到的问题及解决方案,部分是根据项目原因提出的解决办法,可能并不适用于所有人,如有问题,可及时沟通,共同成长。

1、设置启动页防止白屏问题

android需要在文件下修改luanch_background.xml文件,注意drawable和drawable-v21文件夹的区别

ios 需要在ios原生工程下xcode创建luanchScreen,使用storyboard生成跟闪屏页相同的页面

flutter 部分最好重新做一个相同的闪屏页面,注意页面的适配问题,否则会出现从原生页面过来后图标跳动的问题

2、需要在应用启动时判断是否登录状态跳转不同的页面?

因为在android文件夹下修改了启动页,这里又要在dart里面判断是否登录状态,所以需要在dart这边又要重新弄一个启动页,才能加以判断,所以这里会有两个启动页,目前还没有其他方案

3、启动页需要图片全屏显示

有三种解决方式:我使用第三种方式解决

1、通过 ConstrainedBox 的 constraints 属性设置图片充满全屏

ConstrainedBox(
  constraints: BoxConstraints.expand(), // 设置延伸自适应屏幕
  child: Image.asset(
    "assets/images/xxx.png",
     fit: BoxFit.cover,
  ),
)

2、使用Container包裹设置宽高为屏幕的宽高

Container(
  width: MediaQuery.of(context).size.width, // 屏幕宽度
  height: MediaQuery.of(context).size.height, // 屏幕高度
  child: Image.asset(
    "assets/images/xxx.png",
     fit: BoxFit.cover,
  ),
)

3、如果使用了层级组件Stack,则可以这样设置

Stack(
  children: [
    Positioned.fill(
      child: Image.asset(
        "assets/images/xxx.png",
         fit: BoxFit.cover,
      ),
    ),
  ],
)

4、TextField去掉默认的下划线

可以设置decoration的属性的border属性为InputBorder.none即可去掉下划线

TextField(
  decoration: InputDecoration(
    border: InputBorder.none,
  ),
)

5、TextField设置maxlength会在右下角显示计数问题

TextField(
  decoration: InputDecoration(
    //maxLength: 6 //不设置
    //使用该方式限制输入长度
    inputFormatters: [LengthLimitingTextInputFormatter(6)],
  ),
)

6、textField在row或colunm中时不设置宽高会显示有误,需要再套一层container设置宽高即可

7、webView中的url需要经过Future处理后再加载,加载的地址不正确:webView加载的是处理之前的url???

目前的解决方案是提前处理url拼接相关参数

8、SingleChildScrollView和ListView、GridView等控件嵌套使用

需要在子控件(ListView或GridView)中添加一下属性:不滚动子控件

shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
                

9、GridView使用时需要添加childAspectRatio(宽高比参数)意义在哪??

这是flutter使用GridView必须添加参数,可以根据设计的宽高来写比列,注意适配问题

10、http接口请求报错问题

url请求的地址为http地址,需要在原生项目中添加允许http请求

11、在getX生成的页面中使用TabController?

因为getX生成的页面是stl无状态页面,如果需要使用tabController,就需要用一个stf将该页面包裹起来使用

12、两个Text文字无法对齐

试着将两个text的行高设置成一致,根据实际情况处理,也可以参考我之前的文章《flutter中 文字对齐方案》

Text(
  style:TextStyle(
    height: 1.1
  )
)

13、TextField在ios上双击或者长按显示灰屏

需要在runapp中设置多语言适配

    localizationsDelegates: const [
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate,
      ChineseCupertinoLocalizations.delegate, // 自定义的delegate
      DefaultCupertinoLocalizations.delegate, // 目前只包含英文
    ],
    supportedLocales: const [
      Locale('zh', 'CN'),
      Locale('zh', 'Hans'), // China
      Locale('zh', ''), // China
      Locale('en', 'US'),
    ],

14、Text在Container中文字不居中显示

需要设置Text的strutStyle

    Text(
      button2,
      strutStyle: StrutStyle(
        fontSize: 13.sp,
        leading: 0,
        // 1.1更居中
        forceStrutHeight: true, // 关键属性 强制改为文字高度
      ),
      style: TextStyle(
          color: MyColors.colord9000000,
          fontSize: 14.sp,
          fontWeight: FontWeight.bold),
    ),

15、ListView和GiridView会默认带padding(top:8),去掉paddding即可 padding(top:0)

16、Flutter中Timer.periodic在后台会暂停问题

目前发现Timer.periodic在安卓中是不会有任何问题,但在ios的release模式中会有问题,所以为了统一处理需要在倒计时控件中监听app的前后台状态,且计算用户在后台经过了多长时间,部分代码如下

class _CountDownWidgetState extends State<CountDownWidget> with WidgetsBindingObserver{
  var _timer;
  int endTime = 0;
  int inTime = 0;//进来的时间
  int outTime = 0;//退出的时间
  @override
  void initState() {
    WidgetsBinding.instance?.addObserver(this);
    endTime = widget.validTime;
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if(mounted){
        setState(() {
          if(endTime < 1){
            if (widget.isFinish != null) {
              widget.isFinish!(true);
            }
            timer.cancel();
          }else{
            endTime -= 1;
          }
        });
      }
    });
    super.initState();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch(state){
      case AppLifecycleState.inactive://不活跃
        //进入后台先走inactive
        print("不活跃状态");
        break;
      case AppLifecycleState.resumed://应用程序可见,前台
        //刚进入页面不走这里,再次进来会走这里
        inTime = DateTime.now().millisecondsSinceEpoch;
        print("前台:$inTime");
        var lastTime = inTime - outTime;//用户进入后台的时间
        print("用户进入后台一共:$lastTime 毫秒");
        if(mounted){
          setState(() {
            var surplusTime = endTime - lastTime ~/ 1000;
            print("进入前台重新计算时间:$endTime , $surplusTime");
            if(surplusTime < 1){
              endTime = 0;
              //直接调用定时器到期方法
              if (widget.isFinish != null) {
                widget.isFinish!(true);
              }
              _timer.cancel();
            }else{
              endTime = surplusTime;
            }
          });
        }
        break;
      case AppLifecycleState.paused://不可见,后台
        //进入后台再走inactive
        outTime = DateTime.now().millisecondsSinceEpoch;
        print("后台:$outTime");
        break;
      case AppLifecycleState.detached://不活跃
        print("detached");
        break;
    }
  }

  @override
  void dispose() {
    if (!mounted || _timer?.isActive == true) {
      _timer?.cancel();
    }
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }
  
   @override
  Widget build(BuildContext context) {
      //这里写Widget
      return Container();
  }
}