前言:
首先说明下,这个总结并非把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
这里只对使用过程中个人觉得比较有记录意义的问题的总结。
image在安装flutter之后,打算开启一个demo测试一下,结果出现了这么一句话:
Unable to locate a development device; please run ‘flutter doctor’ for information about installing additional components.
解决方案:
这些问题都是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
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);
}
}
网友评论