前言:
首先說明下,這個(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.

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

報(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
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);
}
}