美文网首页Flutter圈子
flutter 开发过程中遇到的问题总结

flutter 开发过程中遇到的问题总结

作者: 颜色不一样的炮仗 | 来源:发表于2021-01-22 11:22 被阅读0次

    国际化下光标无法对齐

    theme: ThemeData(
          primaryColor: Style.primaryColor,
          accentColor: Style.accentColor,
          indicatorColor: Colors.white,
          textTheme: TextTheme(
              subhead: TextStyle(
                  textBaseline: TextBaseline.alphabetic) //解决光标跟hinText无法对齐的问题
              )
            // scaffoldBackgroundColor: Style.bgColor,//全局scaffold背景色
            // primarySwatch: Style.primarySwatch,
          ),
    

    初始化数据存储插件

    不进行初始化操作 会在页面渲染时无法获取到存储的数据
    void initSp() async {
        await SpUtil.getInstance();
        if(!mounted) return;
    }
    

    dialog在页面未完成加载前弹出会报错 需要在页面完成加载之后再弹出

    WidgetsBinding.instance.addPostFrameCallback((_) {
     
    });
    

    嵌套路由

    Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          Expanded(
              child: Navigator(
                  initialRoute: 'addBank',
                  onGenerateRoute: (RouteSettings settings) {
                    WidgetBuilder builder;
                    switch (settings.name) {
                      case 'addBank':
                        builder = (BuildContext context) => AddBankPage();
                        break;
                      case 'selfwithdraw':
                        builder = (BuildContext context) => WithdrawPage();
                        break;
                    }
                    return new MaterialPageRoute(
                        builder: builder, settings: settings);
                  }))
        ],
      ),
    );
    

    }

    路由传参
    Navigator.of(context).pushNamed("second ", arguments: " from first page");
    
    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
    //取出路由参数
        String msg = ModalRoute.of(context).settings.arguments as String; 
         …  //数据处理
      }
    }
    

    路由监听

    Widget build(BuildContext context) {
        return BotToastInit(
          child: MaterialApp(
            title: '八八棋牌',
            locale: Locale("zh"),
            debugShowCheckedModeBanner: false, //是否显示debug模式
            localizationsDelegates: [
              GlobalWidgetsLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              const FallbackCupertinoLocalisationsDelegate(),
            ],
            navigatorObservers: [
              BotToastNavigatorObserver(),
              UserNavigatorObserver(context)
            ],
            supportedLocales: [const Locale('zh', 'CH'), const Locale('en', 'US')],
            initialRoute: '/',
            routes: {
              '/MainPage': (ctx) => HomePage(),
            },
           
            home: SplashScreen(),
          ),
        );
      }
    }
    
    class UserNavigatorObserver extends NavigatorObserver {
      // BuildContext _context;
      AppBloc ab;
      UserNavigatorObserver(BuildContext context) {
        ab = BlocProvider.of<AppBloc>(context);
      }
    
      static List<Route<dynamic>> history = <Route<dynamic>>[];
    
      @override
      void didPop(Route route, Route previousRoute) {
        super.didPop(route, previousRoute);
        history.remove(route);
        print("UserNavigatorObserver didPush route ${route.settings.toString()} "
            "previousRoute ${previousRoute?.settings?.name}");
            
        //String routeName = previousRoute?.settings?.name;
        //if (routeName == '/MainPage') {
        //  ab.sendTriggerAnimate('forward');
        //}
    
        ///调用Navigator.of(context).pop() 出栈时回调
      }
    
      @override
      void didPush(Route route, Route previousRoute) {
        super.didPush(route, previousRoute);
        history.add(route);
        // print("UserNavigatorObserver didPush route ${route.settings.toString()} "
        //     "previousRoute ${previousRoute?.settings?.name}");
        // print("UserNavigatorObserver didPush _history: ${history.length}");
        // print('==========previousRoute.settings.name====
    
        ///调用Navigator.of(context).push(Route()) 进栈时回调
      }
    
      @override
      void didRemove(Route route, Route previousRoute) {
        super.didRemove(route, previousRoute);
        history.remove(route);
        // print("UserNavigatorObserver didRemove route ${route.settings.toString()} "
        //     "previousRoute ${previousRoute?.settings?.toString()}");
        // print("UserNavigatorObserver didRemove _history: ${history.length}");
    
        ///调用Navigator.of(context).removeRoute(Route()) 移除某个路由回调
      }
    
      @override
      void didReplace({Route newRoute, Route oldRoute}) {
        super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
        history.remove(oldRoute);
        history.add(newRoute);
        // print(
        //     "UserNavigatorObserver didReplace route ${newRoute.settings.toString()} "
        //     "previousRoute ${oldRoute?.settings?.toString()}");
    
        ///调用Navigator.of(context).replace( oldRoute:Route("old"),newRoute:Route("new)) 替换路由时回调
      }
    
      @override
      void didStartUserGesture(Route route, Route previousRoute) {
        super.didStartUserGesture(route, previousRoute);
        // print(
        //     "UserNavigatorObserver didStartUserGesture route ${route.settings.toString()} "
        //     "previousRoute ${previousRoute?.settings?.toString()}");
    
        ///iOS侧边手势滑动触发回调 手势开始时回调
      }
    
      @override
      void didStopUserGesture() {
        super.didStopUserGesture();
        print("UserNavigatorObserver didStopUserGesture ");
    
        ///iOS侧边手势滑动触发停止时回调 不管页面是否退出了都会调用
      }
    }
    

    返回刷新页面

    方法一
    @override
    void deactivate() {
        var bool = ModalRoute.of(context).isCurrent;
        if (bool) {
            getData();
        }
    }
    
    方法二
    NavigatorUtil.navigatorRouter(context, UpdateNickPage()).then((data) {
        ......
    })
    
    跳转的页面
    Navigator.pop(context, params);
    

    点击空白区域 关闭软键盘

    GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          FocusScope.of(context).requestFocus(FocusNode());
        },
    

    自定义键盘时禁止软键盘弹出 光标显示

    // 设置 readOnly: true,
    // 设置 showCursor: true,
     Container(
            height: 25,
            width: 80,
            child: TextField(
              controller: widget.controller,
              focusNode: widget.focusNode,
              keyboardType: TextInputType.number,
              textAlign: TextAlign.center,
              textAlignVertical: TextAlignVertical.center,
              inputFormatters: [WhitelistingTextInputFormatter.digitsOnly],
              readOnly: true,
              showCursor: true,
              onTap: () {},
              onChanged: widget.onChanged,
              style: TextStyle(
                  color: Colours.fontColor_black,
                  fontSize: ScreenUtil().setSp(40)),
              decoration: InputDecoration(
                border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(4),
                    borderSide: BorderSide.none),
                fillColor: Color.fromRGBO(248, 248, 248, 1),
                filled: true,
                contentPadding: EdgeInsets.fromLTRB(2, 2, 2, 10),
              ),
            ),
          ),
    
    光标随着输入移动
     numberCtrl.selection = TextSelection.fromPosition(TextPosition(offset: numberCtrl.text.length));
    

    InheritedWidget使用场景

    当小部件嵌套 而不是所有的小部件都需要传入参数
    class MyInheritedWidget extends InheritedWidget {
      final Map payPageConfig;
      final dynamic pageType;
      MyInheritedWidget({
        Key key,
        @required this.payPageConfig,
        @required this.pageType,
        @required Widget child,
      }) : super(key: key, child: child);
      static MyInheritedWidget of(BuildContext context) {
        return context.inheritFromWidgetOfExactType(MyInheritedWidget);
      }
    
      @override
      bool updateShouldNotify(MyInheritedWidget old) =>
          payPageConfig != old.payPageConfig;
    }
    
    MyInheritedWidget(
      payPageConfig: f,
      pageType: f['pageType'],
      child: AliPay(),
    )
    
     Widget build(BuildContext context) {
    final inheritedContext = MyInheritedWidget.of(context);
    return Container(
      padding: EdgeInsets.only(left: 10, right: 0, top: 5),
      child: AliPayQuotaPage(
          payConfig: inheritedContext.payPageConfig,
          pageType: inheritedContext.pageType,
        ),
    );
    

    }

    在滚动容器singleChildScrollView 下使用Expanded

    LayoutBuilder(
      builder: (context, constraint) {
        return SingleChildScrollView(
          child: ConstrainedBox(
            constraints: BoxConstraints(minHeight: constraint.maxHeight),
            child: IntrinsicHeight(
              child: Column(
                children: <Widget>[
                  Text("Header"),
                  Expanded(
                    child: Container(
                      color: Colors.red,
                    ),
                  ),
                  Text("Footer"),
                ],
              ),
            ),
          ),
        );
      },
    )
    

    Dio 请求拦截

    使用Interceptor进行Flutter刷新获取新的token
      Future<Dio> getApiClient() async {
        dio.interceptors.clear();
        dio.interceptors
            .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
          // Do something before request is sent
          options.headers["Authorization"] = "Bearer " + getToken();
          return options;
        }, onResponse: (Response response) async {
          // Do something with response data
          if (response.data['code'] == 202) {
            dio.interceptors.requestLock.lock();
            dio.interceptors.responseLock.lock();
            
            RequestOptions options = response.request;
            options.headers["Authorization"] = "Bearer " + await getRefreshToken();
            dio.interceptors.requestLock.unlock();
            dio.interceptors.responseLock.unlock();
            return dio.request(options.path, options: options);
          } else {
            return response;
          }
          // return response; // continue
        }, onError: (DioError error) async {
          return error;
        }));
        dio.options.baseUrl = baseUrl;
        return dio;
      }
    }
    Future<String> getRefreshToken() async { 
      String refreshToken;
      Dio tokenDio = new Dio(); //创建新的Dio实例
      tokenDio.options.headers['Authorization'] =
          "Bearer " + NetUtil.getToken(); //设置当前的refreshToken
      try {
        String url = Config.server + '/User/ReplaceToken'; //refreshToken url
        var response = await tokenDio.post(url).catchError((onError) {
          print(onError);
        }); //请求refreshToken刷新的接口
        if (response.data['status']) {
          refreshToken = response.data['data']; //新的accessToken
          SpUtil.putString('token', refreshToken);
        }
        print('refreshToken===========$refreshToken');
      } on DioError catch (e) {}
      return refreshToken;
    }
    

    多嵌套路由 showDialog 关闭问题

     当多个Navigator嵌套在App中时.由showDialog(...)方法创建的对话框路由·      被推送到根导航器.如果应用程序有多个Navigator对象,可能需要调用
        Navigator.of(context, rootNavigator: true).pop(result)
    来关闭对话框而不仅仅是Navigator.pop(context, result).
    

    如果在2个对话框在1个屏幕上打开时遇到问题,则只需在此Future中放置一个对话框即可

    Future.delayed(Duration(seconds: 1), () {
                Navigator.of(context, rootNavigator: true).pop(true);
              });        
    

    检查是否有网络

    Future checkInternetState(currentContext, isLoding) async {
      try {
        String host = "baidu.com"; //判断国内外,谷歌还是百度
        final result = await InternetAddress.lookup(host);
        if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
          print("Network is ok.");
          return true;
        }
      } on SocketException catch (_) {
        print("Network is error.");
        if (isLoding) {
          Navigator.pop(currentContext);
        }
        Util.tip('网络无连接', type: 'warning');
        // Navigator.push(currentContext,
        //     new MaterialPageRoute(builder: (BuildContext context) {
        //   return NetErrorPage();
        // }));
        return false;
      }
    }
    

    获取widget的坐标和大小

    RenderBox renderBox = widget.popKey.currentContext.findRenderObject();
    //targetWidget的坐标位置
    Offset localToGlobal = renderBox.localToGlobal(Offset.zero);
    // localToGlobal.dx X坐标
    // localToGlobal.dy y坐标
    //targetWidget的大小
    Size size = renderBox.size; //targetWidget的size
    // size.width 宽
    // size.height 高
    

    flutter Column 中包含ListView height如果不设置 界面显示会有问题,如果要设置,又不能准确的计算出结果

    在外面包一层 Expanded 或者 Flexible 可解决问题
    Column(children: <Widget>[
      Text(“测试”),
      Expanded(child:  Container(
          width: ScreenUtil().setWidth(570),
        child: ListView.builder(
          scrollDirection: Axis.vertical,
          
           itemBuilder:
                  (context,index){
    
            return _getListItem(index);
          },
            itemCount: list.length,
    
          ),
        )),
    ],)
    

    dart 相关语法

    class User {
        String name;
        void print () {
            print('${this.name}');
        }
        
    }
    
    ?.是什么意思
    void foo (User user) {
        user?.print()    
    }
    
    void foo (User user) {
        if(user != null) {
            user.print();
        }
    }
    
    ?? 是什么意思
    var text = user?.name ?? '测试文字' 
    
    String text;
    if(user != null && user.name != null) {
        text = user.name;
    }else{
        text = '测试文字'
    }
    
    ??= 是什么意思
    User create (User user) {
        var user ?? = User() ;
        return user;
    }
    
     User create (User user) {
        if(user == null) {
            user =  User();
        }
        return user;
    }
    

    _formKey.currentState.reset() 重置表单无效原因

    InheritedWidget内部的对象无法更改值
    

    获取状态栏高度

    kToolbarHeight
    MediaQueryData.fromWindow(window).padding.top
    

    获取软键盘高度

    keyBoardHeight = MediaQuery.of(context).viewInsets.bottom;
    

    Flutter SingleChildScrollView代码控制滑动到底部或顶部

    通过代码将SingleChildScrollView滑动到底部只需要两个步骤
    SingleChildScrollView设置一个ScrollController
    ScrollController调用jumpTo控制SingleChildScrollView互动到底部
    如下实例代码,点击右下角FloatingActionButton将SingleChildScrollView滑动到底部
    scrollController.position.maxScrollExtent获取的是底部的位置
    同理,scrollController.position.minScrollExtent可以获取顶部位置
    

    滚动容器的滚动监听

    滚动监听及控制

    scrollController.position.pixels //当前滚动位置
    scrollController.position.maxScrollExtent //最大滚动长度
    

    如何使列表的高度增加到达到定义的最大高度,然后在溢出时显示滚动条

    Expanded(
      child: ListView.builder(
        itemCount: _items.length,
        itemBuilder: (context, index) => _buildItem(_items[index]),
        shrinkWrap: true,
      )
    ),
    

    flutter_web 选择文件上传

    pickImage() {
        final html.InputElement input = html.document.createElement('input');
        input
          ..type = 'file'
          ..accept = 'image/*';
    
        input.onChange.listen((e) {
          if (input.files.isEmpty) return;
          final reader = html.FileReader();
          reader.readAsDataUrl(input.files[0]);
          reader.onError.listen((err) => setState(() {
                // error = err.toString();
              }));
          reader.onLoad.first.then((res) {
            final encoded = reader.result as String;
            // remove data:image/*;base64 preambule
            final stripped =
                encoded.replaceFirst(RegExp(r'data:image/[^;]+;base64,'), '');
            print(stripped);
            setState(() {
              // name = input.files[0].name;
              // bytes = base64.decode(stripped);
              // error = null;
            });
          });
        });
    
        input.click();
    }
    

    flutter_inappwebview js通讯

    html 代码
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        </head>
        <body>
            <h1>JavaScript Handlers (Channels) TEST</h1>
            <button id="btn"></button>
            <script>
                window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
                    document.getElementById('btn').onclick = function() {
                        window.flutter_inappwebview 
                          .callHandler('handlerFooWithArgs', 1, true, ['isBack', true]);  
                    } 
                   
                });
            </script>
        </body>
    </html>
    
    flutter 代码
    InAppWebView(
      initialUrl: widget.url,
      initialOptions: InAppWebViewGroupOptions(
          crossPlatform: InAppWebViewOptions(
        debuggingEnabled: true,
        javaScriptEnabled: true,
        useShouldOverrideUrlLoading: true,
        useOnLoadResource: true,
        cacheEnabled: true,
      )),
      onWebViewCreated: (InAppWebViewController controller) {
        webView = controller;
        webView.addJavaScriptHandler(
            handlerName: 'handlerFooWithArgs',
            callback: (args) {
              print(args);
              Navigator.of(context).pop("点击了取消");
              // it will print: [1, true, [bar, 5], {foo: baz}, {bar: bar_value, baz: baz_value}]
            });
      },
      onLoadStart: (InAppWebViewController controller, String url) {
        setState(() {
          widget.url = url;
        });
      },
      onLoadStop: (InAppWebViewController controller, String url) async {
        String _js = '''
          if (!window.flutter_inappwebview.callHandler) {
              window.flutter_inappwebview.callHandler = function () {
                  var _callHandlerID = setTimeout(function () { });
                  window.flutter_inappwebview._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));
                  return new Promise(function (resolve, reject) {
                      window.flutter_inappwebview[_callHandlerID] = resolve;
                  });
              };
          }
        ''';
        controller.evaluateJavascript(source: _js);
        setState(() {
          widget.url = url;
        });
      },
      onProgressChanged:
          (InAppWebViewController controller, int progress) {},
    ),
    

    xcode app 启动强制横屏 打开app后可以横竖屏

    info.plist 添加代码
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    
    AppDelegate 添加代码
    override  func application(
    _ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
       return .allButUpsideDown
    }
    

    andriod 打包后apk检测到病毒

    因为flavor 分渠道打包引发的问题 在Andriod9 版本之后需要
    在build.gradle 的defaultConfig 中加上 flavorDimensions "versionCode"

    defaultConfig {
        targetSdkVersion:***
        minSdkVersion :***
        versionCode:***
        versionName :***
        //版本名后面下面这句话,意思就是flavor dimension它的维度就是该版本号,这样维度就是都是统一的
        flavorDimensions "versionCode"
    }
    

    相关文章

      网友评论

        本文标题:flutter 开发过程中遇到的问题总结

        本文链接:https://www.haomeiwen.com/subject/kejszktx.html