Flutter踩坑

前言:
首先說明下,這個(gè)總結(jié)并非把flutter完完整整的api全部總結(jié)一遍,網(wǎng)上有很多資料。比如
flutter中文網(wǎng)地址https://flutterchina.club/setup-windows/
阿里前端技術(shù)人員的項(xiàng)目例子 https://github.com/alibaba/flutter-go
[flutter常用的插件總結(jié)https://www.cnblogs.com/yangyxd/p/9232308.html]
https://github.com/AweiLoveAndroid/Flutter-learning(https://www.cnblogs.com/yangyxd/p/9232308.html)

項(xiàng)目地址: https://gitee.com/rkwzw/flutter-master

這里只對(duì)使用過程中個(gè)人覺得比較有記錄意義的問題的總結(jié)。

在安裝flutter之后,打算開啟一個(gè)demo測(cè)試一下,結(jié)果出現(xiàn)了這么一句話:
Unable to locate a development device; please run ‘flutter doctor’ for information about installing additional components.

image

解決方案:

這些問題都是Android sdk找不到的原因,只要新建一個(gè) ANDROID_HOME: 你的Android sdk存放的路徑 并在path環(huán)境中添加 sdk tool和 platforms-tool是的環(huán)境。

image

報(bào)錯(cuò) :
The following assertion was thrown building Text("1111"):
No Directionality widget found.

解決方案:
給Text添加一個(gè)TextDirection。
例如:

children: <Widget>[
        ///flex默認(rèn)為1
        new Expanded(child: new Text("1111",textDirection: TextDirection.ltr,), flex: 2,),
        new Expanded(child: new Text("2222",textDirection: TextDirection.ltr,)),
      ],

flutter 庫(kù)地址https://flutterawesome.com/
flutter 添加iconfont圖片
https://blog.csdn.net/heshuncheng/article/details/107039880
點(diǎn)擊事件:
1、如果Widget支持事件監(jiān)聽,則可以將一個(gè)函數(shù)傳遞給它并進(jìn)行處理。例如,RaisedButton有一個(gè)onPressed參數(shù)

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

2、如果Widget不支持事件監(jiān)聽,則可以將該Widget包裝到GestureDetector中,并將處理函數(shù)傳遞給onTap參數(shù)

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('跳轉(zhuǎn)')),
              ),
            )

7 TextFiled屬性詳解
http://www.itdecent.cn/p/4035c2c34bb4

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

8 布局技巧一
https://zhuanlan.zhihu.com/p/38140107
設(shè)置占滿一行

                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 關(guān)于 TextFiled
https://blog.csdn.net/yuzhiqiang_1993/article/details/88204031
10 TabBar屬性
https://blog.csdn.net/wenwst/article/details/102796994
11 關(guān)于 row colum
http://www.itdecent.cn/p/0ce74751d970
https://cloud.tencent.com/developer/article/1353776
12 Horizontal viewport was given unbounded height.
listview上面如果有row colum 經(jīng)常出現(xiàn)以上問題。即需要設(shè)置確定的寬高
以橫向listview為例子 在外部加一個(gè)container并設(shè)置高度即可

            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點(diǎn)擊空白區(qū)域沒反應(yīng)
GestureDetector添加屬性behavior: HitTestBehavior.opaque,
14 Flutter 啟動(dòng)頁(yè)白屏設(shè)置/啟動(dòng)畫面設(shè)置
https://blog.csdn.net/dimonds90/article/details/105213551
15 Flutter 路由管理(頁(yè)面跳轉(zhuǎn))
https://blog.csdn.net/u013095264/article/details/101556346

遇到Flutter Navigator.pop黑屏問題最終的解決方案是:
https://www.uedbox.com/post/65041/
一個(gè)程序只能有一個(gè)MaterialApp存在,其它都應(yīng)該使用Scaffold包含,如果你使用多個(gè)MaterialApp也不會(huì)報(bào)錯(cuò),但就會(huì)出現(xiàn)類似跳轉(zhuǎn)黑屏這種問題。同時(shí)也可能會(huì)報(bào)如下錯(cuò)誤:

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(
                  "返回上一個(gè)頁(yè)面并附帶一個(gè)值",
                  style: TextStyle(
                    fontSize: 15,
                  ),
                ),onPressed:(){
                  Navigator.pop(context,"第二個(gè)頁(yè)面返回");
            } ,),

          ],
        ),
      ),
    );
  }
}

16多重滑動(dòng)沖突問題
在column中添加一個(gè)整體可滾動(dòng)且內(nèi)嵌一個(gè)listview/gridview
首先需要添加一個(gè)Expanded, 其次 需要設(shè)置列表不讓滾動(dòng)
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 沉浸式
沉浸式主要是純色和圖片沉浸式的樣式
純色: 通過調(diào)用 SystemChrome.setSystemUIOverlayStyle(customSystemUiOverlayStyle);方法設(shè)置狀態(tài)欄、導(dǎo)航欄等顏色
同時(shí)需要在布局中先添加safeArea()用于解決布局頂?shù)綄?dǎo)航欄。

static void statusColorBar({Color color}) {
    //沉浸式狀態(tài)欄
    SystemUiOverlayStyle customSystemUiOverlayStyle = SystemUiOverlayStyle(
      systemNavigationBarColor: color,
      //系統(tǒng)底部導(dǎo)航,即虛擬導(dǎo)航鍵那一塊
      systemNavigationBarDividerColor: null,
      //分隔條顏色
      statusBarColor: color,
      //狀態(tài)欄顏色
      systemNavigationBarIconBrightness: Brightness.light,
      //系統(tǒng)導(dǎo)航圖標(biāo)的亮度
      statusBarIconBrightness: Brightness.light,
      //頂部狀態(tài)欄圖標(biāo)的亮度
      statusBarBrightness: Brightness.light, //頂部狀態(tài)欄的亮度
    );
    SystemChrome.setSystemUIOverlayStyle(customSystemUiOverlayStyle);
  }

18Flutter BottomNavigationBar切換會(huì)刷新當(dāng)前頁(yè)面的解決方案

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

圖片:暫時(shí)沒有好的解決方案,如果頭部是一個(gè)圖片,需要導(dǎo)航欄也是圖片沉浸式則可以通過不設(shè)置SafeArea()的方法默認(rèn)將圖片頂?shù)綄?dǎo)航欄以達(dá)到沉浸式效果。
19 No Material widget found Switch widgets require a Material widget ancestor
https://blog.csdn.net/Timmy_zzh/article/details/88770095

20 Flutter 漸變色高級(jí)用法
https://www.cnblogs.com/mengqd/p/13237789.html
21 Flutter 出現(xiàn)has different version for the compile (1.0.0) and runtime (1.0.1) classpath
https://www.codeleading.com/article/6267837139/
22 http網(wǎng)絡(luò)庫(kù)
https://www.cnblogs.com/zhujiabin/p/10333253.html
23 flutter和Android原生交互
Android端
添加一個(gè)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:去請(qǐng)求權(quán)限后再獲取
            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;
            }
        }
        // 在一些手機(jī)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;
    }


}
注冊(cè)該插件類

其中register頭部固定寫法,switch中為具體的方法,有返回值記得添加 result.success(entity),沒有返回值直接設(shè)置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() {
    //內(nèi)部初始化
    //init
  }

  static AndroidFlutterPlugin _getInstance() {
    if (_instance == null) {
      _instance = new AndroidFlutterPlugin._internal();
    }
    return _instance;
  }

  //這里初始化通道,里面存放一個(gè)name,用來和Android進(jìn)行交互
  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端的方法

調(diào)用

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("獲取到的數(shù)據(jù) $data");

        return jsonDecode(data);
      } else {
        print(response.statusCode);
      }
    } catch (e) {
      print(e);
    }
  }

持續(xù)更新中

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容