美文网首页
Flutter踩坑

Flutter踩坑

作者: 若无初见 | 来源:发表于2019-03-01 08:49 被阅读0次

    前言:
    首先说明下,这个总结并非把flutter完完整整的api全部总结一遍,网上有很多资料。比如
    flutter中文网地址https://flutterchina.club/setup-windows/
    阿里前端技术人员的项目例子 https://github.com/alibaba/flutter-go
    [flutter常用的插件总结https://www.cnblogs.com/yangyxd/p/9232308.html]
    https://github.com/AweiLoveAndroid/Flutter-learning(https://www.cnblogs.com/yangyxd/p/9232308.html)

    项目地址: https://gitee.com/rkwzw/flutter-master

    这里只对使用过程中个人觉得比较有记录意义的问题的总结。

    在安装flutter之后,打算开启一个demo测试一下,结果出现了这么一句话:
    Unable to locate a development device; please run ‘flutter doctor’ for information about installing additional components.

    image

    解决方案:

    这些问题都是Android sdk找不到的原因,只要新建一个 ANDROID_HOME: 你的Android sdk存放的路径 并在path环境中添加 sdk tool和 platforms-tool是的环境。

    image

    报错 :
    The following assertion was thrown building Text("1111"):
    No Directionality widget found.

    解决方案:
    给Text添加一个TextDirection。
    例如:

    children: <Widget>[
            ///flex默认为1
            new Expanded(child: new Text("1111",textDirection: TextDirection.ltr,), flex: 2,),
            new Expanded(child: new Text("2222",textDirection: TextDirection.ltr,)),
          ],
    

    flutter 库地址https://flutterawesome.com/
    flutter 添加iconfont图片
    https://blog.csdn.net/heshuncheng/article/details/107039880
    点击事件:
    1、如果Widget支持事件监听,则可以将一个函数传递给它并进行处理。例如,RaisedButton有一个onPressed参数

    @override
    Widget build(BuildContext context) {
      return new RaisedButton(
          onPressed: () {
            print("click");
          },
          child: new Text("Button"));
    }
    

    2、如果Widget不支持事件监听,则可以将该Widget包装到GestureDetector中,并将处理函数传递给onTap参数

    class SampleApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
            body: new Center(
          child: new GestureDetector(
            child: new FlutterLogo(
              size: 200.0,
            ),
            onTap: () {
              print("tap");
            },
          ),
        ));
      }
    }
    

    3、Flutter :MediaQuery.of() called with a context that does not contain a MediaQuery

    4、Flutter Text 有黄色下划线

    5、Flutter SafeArea解决刘海屏等显示问题

    6、控件显示隐藏

      Offstage(
                  //true 隐藏 false 显示
                  offstage: false,
                  child:  Padding(
                    padding: EdgeInsets.only(top: 10),
                    child: FlatButton(
                        onPressed:(){
                          Navigator.of(context).push(MaterialPageRoute(builder: (context){
                            return new MyApp();
                          }));
                        } ,
                        child: Text('跳转')),
                  ),
                )
    

    7 TextFiled属性详解
    https://www.jianshu.com/p/4035c2c34bb4

    Flutter中TextField的高度约束
    https://www.caoxiaozhu.com/index.php/archives/116/

    8 布局技巧一
    https://zhuanlan.zhihu.com/p/38140107
    设置占满一行

                    Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          FlatButton(
                            padding: EdgeInsets.only(top: 10, bottom: 10),
                            onPressed: _login,
                            color: Color(0xff3C98FF),
                            child: Text(
                              '登录',
                              style: TextStyle(color: Colors.white, fontSize: 18),
                            ),
                            shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(22.5)),
                          )
                        ],
                      ),
    

    9 关于 TextFiled
    https://blog.csdn.net/yuzhiqiang_1993/article/details/88204031
    10 TabBar属性
    https://blog.csdn.net/wenwst/article/details/102796994
    11 关于 row colum
    https://www.jianshu.com/p/0ce74751d970
    https://cloud.tencent.com/developer/article/1353776
    12 Horizontal viewport was given unbounded height.
    listview上面如果有row colum 经常出现以上问题。即需要设置确定的宽高
    以横向listview为例子 在外部加一个container并设置高度即可

                Container(
                        height: 50,
                        child: ListView.builder(
                          scrollDirection: Axis.horizontal,
                          itemCount: 10,
                          itemBuilder: (BuildContext context, int index) {
                            return Container(
                              width: 40,
                              height: 40,
                              margin: EdgeInsets.all(5),
                              decoration: BoxDecoration(
                                  shape: BoxShape.rectangle,
                                  color: Colors.red,
                                  borderRadius: BorderRadius.circular(10.0),
                                  image: DecorationImage(
                                      image: AssetImage('images/icon_header.jpg'),
                                      fit: BoxFit.fill)),
                            );
                          },
                        ),
                      ),
    

    13 GestureDetector点击空白区域没反应
    GestureDetector添加属性behavior: HitTestBehavior.opaque,
    14 Flutter 启动页白屏设置/启动画面设置
    https://blog.csdn.net/dimonds90/article/details/105213551
    15 Flutter 路由管理(页面跳转)
    https://blog.csdn.net/u013095264/article/details/101556346

    遇到Flutter Navigator.pop黑屏问题最终的解决方案是:
    https://www.uedbox.com/post/65041/
    一个程序只能有一个MaterialApp存在,其它都应该使用Scaffold包含,如果你使用多个MaterialApp也不会报错,但就会出现类似跳转黑屏这种问题。同时也可能会报如下错误:

    flutter: Another exception was thrown: Could not find a generator for route RouteSettings

    解决方法也是一样的。

    import 'package:flutter/material.dart';
    
    class MainPage extends StatelessWidget {
      MainPage({this.title});
    
      final String title;
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          body: new MainPageStatefulWidget(content: title,),
        );
      }
    }
    
    class MainPageStatefulWidget extends StatefulWidget {
      MainPageStatefulWidget({Key key, this.content}) : super(key: key);
    
      final String content;
    
      @override
      State<StatefulWidget> createState() => MainPageState();
    }
    
    class MainPageState extends State<MainPageStatefulWidget> {
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return new Scaffold(
          body: SafeArea(
            child: Column(
              children: <Widget>[
                Text(widget.content),
                FlatButton(
                    textColor: Colors.red,
                    child: Text(
                      "返回上一个页面并附带一个值",
                      style: TextStyle(
                        fontSize: 15,
                      ),
                    ),onPressed:(){
                      Navigator.pop(context,"第二个页面返回");
                } ,),
    
              ],
            ),
          ),
        );
      }
    }
    

    16多重滑动冲突问题
    在column中添加一个整体可滚动且内嵌一个listview/gridview
    首先需要添加一个Expanded, 其次 需要设置列表不让滚动
    shrinkWrap: true,
    physics: NeverScrollableScrollPhysics(),

     Expanded(
              child: SingleChildScrollView(
                child: GridView.builder(
                    shrinkWrap: true,
                    physics: NeverScrollableScrollPhysics(),
                    itemCount: list.length,
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: 5),
                    itemBuilder: (context, index) {
                      print("index $index");
                      return list[index];
                    }),
              ),
            )
    

    17 沉浸式
    沉浸式主要是纯色和图片沉浸式的样式
    纯色: 通过调用 SystemChrome.setSystemUIOverlayStyle(customSystemUiOverlayStyle);方法设置状态栏、导航栏等颜色
    同时需要在布局中先添加safeArea()用于解决布局顶到导航栏。

    static void statusColorBar({Color color}) {
        //沉浸式状态栏
        SystemUiOverlayStyle customSystemUiOverlayStyle = SystemUiOverlayStyle(
          systemNavigationBarColor: color,
          //系统底部导航,即虚拟导航键那一块
          systemNavigationBarDividerColor: null,
          //分隔条颜色
          statusBarColor: color,
          //状态栏颜色
          systemNavigationBarIconBrightness: Brightness.light,
          //系统导航图标的亮度
          statusBarIconBrightness: Brightness.light,
          //顶部状态栏图标的亮度
          statusBarBrightness: Brightness.light, //顶部状态栏的亮度
        );
        SystemChrome.setSystemUIOverlayStyle(customSystemUiOverlayStyle);
      }
    

    18Flutter BottomNavigationBar切换会刷新当前页面的解决方案

    https://www.cnblogs.com/kingbo/p/11430351.html

    图片:暂时没有好的解决方案,如果头部是一个图片,需要导航栏也是图片沉浸式则可以通过不设置SafeArea()的方法默认将图片顶到导航栏以达到沉浸式效果。
    19 No Material widget found Switch widgets require a Material widget ancestor
    https://blog.csdn.net/Timmy_zzh/article/details/88770095

    20 Flutter 渐变色高级用法
    https://www.cnblogs.com/mengqd/p/13237789.html
    21 Flutter 出现has different version for the compile (1.0.0) and runtime (1.0.1) classpath
    https://www.codeleading.com/article/6267837139/
    22 http网络库
    https://www.cnblogs.com/zhujiabin/p/10333253.html
    23 flutter和Android原生交互
    Android端
    添加一个java文件,可以理解成该文件专门写Android原生代码

    package io.flutter.plugins;
    
    import android.Manifest;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.location.Criteria;
    import android.location.Location;
    import android.location.LocationManager;
    import android.text.TextUtils;
    import android.widget.Toast;
    
    import java.lang.invoke.MethodHandle;
    import java.util.HashMap;
    import java.util.List;
    
    import androidx.core.content.ContextCompat;
    import io.flutter.embedding.engine.plugins.FlutterPlugin;
    import io.flutter.plugin.common.BinaryMessenger;
    import io.flutter.plugin.common.MethodCall;
    import io.flutter.plugin.common.MethodChannel;
    
    import static android.content.Context.LOCATION_SERVICE;
    
    /**
     * @author : wangzw
     * @date : 20-12-4上午11:26
     * @desc :
     */
    public class AndroidFlutterPlugin {
    
        private static final String CHANNEL_NAME = "android_flutter_plugin";
    
        public static void register(Context context, BinaryMessenger binaryMessenger) {
            new MethodChannel(binaryMessenger, CHANNEL_NAME).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                @Override
                public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                    switch (methodCall.method) {
                        case "showLongToast":
                            Toast.makeText(context, methodCall.argument(("message")), Toast.LENGTH_LONG).show();
                            result.success(null);
                            break;
                        case "showShortToast":
                            Toast.makeText(context, methodCall.argument(("message")), Toast.LENGTH_SHORT).show();
                            result.success(null);
                            break;
                        case "showToast":
                            // Toast.makeText(context, (String)methodCall.argument("message"), (Integer) methodCall.argument("duration"))
                            result.success(null);
                            break;
    
                        case "openGps":
                            Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                            context.startActivity(intent);
                            result.success(null);
                            break;
    
                        case "getLocation":
                            double[] lastKnownLocation = getLastKnownLocation(context);
                            result.success(lastKnownLocation);
                            break;
                    }
                }
            });
        }
    
        private static double[] getLastKnownLocation(Context context) {
            double[] array = new double[2];
            //获取地理位置管理器
            LocationManager mLocationManager = (LocationManager) context.getApplicationContext().getSystemService(LOCATION_SERVICE);
            if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO:去请求权限后再获取
                return null;
            }
            List<String> providers = mLocationManager.getProviders(true);
            Location bestLocation = null;
            for (String provider : providers) {
                Location l = mLocationManager.getLastKnownLocation(provider);
                if (l == null) {
                    continue;
                }
                if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
                    bestLocation = l;
                }
            }
            // 在一些手机5.0(api21)获取为空后,采用下面去兼容获取。
            if (bestLocation == null) {
                Criteria criteria = new Criteria();
                criteria.setAccuracy(Criteria.ACCURACY_COARSE);
                criteria.setAltitudeRequired(false);
                criteria.setBearingRequired(false);
                criteria.setCostAllowed(true);
                criteria.setPowerRequirement(Criteria.POWER_LOW);
                String provider = mLocationManager.getBestProvider(criteria, true);
                if (!TextUtils.isEmpty(provider)) {
                    bestLocation = mLocationManager.getLastKnownLocation(provider);
                }
            }
    
    
            array[0] = bestLocation.getLongitude();
            array[1] = bestLocation.getLatitude();
            return array;
        }
    
    
    }
    注册该插件类
    

    其中register头部固定写法,switch中为具体的方法,有返回值记得添加 result.success(entity),没有返回值直接设置null

    public class MainActivity extends FlutterActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
        AndroidFlutterPlugin.register(this,getFlutterView());
      }
    
    }
    
    

    Flutter端

    import 'package:flutter/services.dart';
    
    class AndroidFlutterPlugin {
      factory AndroidFlutterPlugin() => _getInstance();
    
      static AndroidFlutterPlugin get instance => _getInstance();
    
      static AndroidFlutterPlugin _instance;
    
      AndroidFlutterPlugin._internal() {
        //内部初始化
        //init
      }
    
      static AndroidFlutterPlugin _getInstance() {
        if (_instance == null) {
          _instance = new AndroidFlutterPlugin._internal();
        }
        return _instance;
      }
    
      //这里初始化通道,里面存放一个name,用来和Android进行交互
      static const channel = MethodChannel('android_flutter_plugin');
    
      void showLongToast(String message) {
        channel.invokeListMethod('showLongToast', {'message': message});
      }
      void showShortToast(String message) {
        channel.invokeListMethod('showShortToast', {'message': message});
      }
      Future<dynamic> openGps() async {
        channel.invokeListMethod('openGps');
      }
      
      Future<dynamic> getLocation() async{
        channel.invokeMethod('getLocation');
      }
    }
    
    

    其中 channel的名字要一样 否则获取不到Android端的方法

    调用

    Future<dynamic> getCityWeather() async {
        List<double> location = await AndroidFlutterPlugin.channel.invokeMethod("getLocation");
        
        var longitude = location[0];
        var latitude = location[1];
        LogUtil.e("location $longitude $latitude");
        //https://api.seniverse.com/v3/weather/now.json?key=SZ52jyZp-52s_mXo-&location=39.93:116.40&language=zh-Hans&unit=c
        var weather = httpGet(
            '$xinZhiWeatherURL?key=$xinZhiApi&language=zh-Hans&unit=c&location=$latitude:$longitude');
        return weather;
      }
    
      Future<dynamic> httpGet(String url) async {
        try {
          http.Response response = await http.get(url);
          print("url $url");
          LogUtil.v("url $url");
          if (response.statusCode == 200) {
            String data = response.body;
            LogUtil.v("获取到的数据 $data");
    
            return jsonDecode(data);
          } else {
            print(response.statusCode);
          }
        } catch (e) {
          print(e);
        }
      }
    

    持续更新中

    相关文章

      网友评论

          本文标题:Flutter踩坑

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