Flutter踩坑

2019-03-01  本文已影响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);
    }
  }

持续更新中

上一篇下一篇

猜你喜欢

热点阅读