最近因工作需要,重温Flutter。2018年初次尝试Flutter后写过从入门到放弃,今年重新学习过后,觉得Flutter已经可以创作想要的App了。
目前对于Flutter出现的问题,Google上基本都可以找到答案了,整体生态链也趋向成熟,唯一不太肯定的是对于Flutter的三方架包的支持,但实在不行也可以使用混合开发解决。
今天就记录下零零碎碎的知识点,针对大些的内容,后面单独写博客记录。
1. 主目录下建资源目录,如:
assets
img
1.5x
start_page_icon_logo.png
2.0x
start_page_icon_logo.png
start_page_icon_logo.png
2. pubspec.yaml下配置
flutter
assets:
- assets/imgs/start_page_icon_logo.png
3. 代码使用
Image.asset('assets/imgs/start_page_icon_logo.png')
初始设置待定,可能需要通过修改主题实现
强制屏幕为横屏
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
强制屏幕为竖屏
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
首先在Image使用时,可以直接使用fit来显示,但这个效果有限,一般都需要在外层套一个容器布局,设定大小,同时也可以再加上Align对齐方式,进行综合布局来设置图片的显示形式,达到Android ImageView的scaleType效果
child: Image.asset(
"assets/imgs/xxx.png",
fit: BoxFit.fitWidth,
),
获取屏幕宽高,Flutter的屏幕适配,使用screenWidth就可以了,不必较真真实的屏幕像素大小,而且在IOS上,计算出来的都是错误的。
screenWidth = MediaQuery.of(context).size.width as int;
screenHeight = MediaQuery.of(context).size.height as int;
print("test");
Text(
"hello flutter!",
textAlign:TextAlign.center,
maxLines:1,
overflow:TextOverflow.ellipsis, // 显示不完,就在后面显示点点
style:TextStyle(
fontSize:30.0, // 文字大小
color:Colors.yellow, // 文字颜色
),
),
color: const Color(0xFF0099ff)
new Align(
alignment: FractionalOffset(0.0, 0.0),
child: Image.asset(
"assets/imgs/xxx.png",
fit: BoxFit.fitWidth,
),
)
alignment可以选择:
FractionalOffset.topLeft
FractionalOffset.topCenter,
FractionalOffset.topRight,
FractionalOffset.centerLeft,
FractionalOffset.center,
FractionalOffset.centerRight,
FractionalOffset.bottomLeft,
FractionalOffset.bottomCenter,
FractionalOffset.bottomRight,
只隐藏顶部状态栏
import 'package:flutter/services.dart';
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
只隐藏底部状态栏
import 'package:flutter/services.dart';
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);
隐藏顶部状态栏和底部操作栏
import 'package:flutter/services.dart';
SystemChrome.setEnabledSystemUIOverlays([]);
第一种,缺点是无法取消
Future.delayed(Duration(seconds: 2), () {
Navigator.of(context, rootNavigator: true).pop();
});
第二种,可以取消
Timer _timer;
@override
Widget build(BuildContext context) {
if (_timer != null) {
_timer.cancel();
}
_timer = Timer(Duration(seconds: 3), () {
// 跳转页面
Navigator.push(
context, MaterialPageRoute(builder: (context) => HomePage()));
});
});
// 跳转不关闭当前页面
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
// 跳转并关闭当前页面
Navigator.pushAndRemoveUntil(
context,
new MaterialPageRoute(builder: (context) => new HomePage()),
(route) => route == null,
);
Container(
decoration: BoxDecoration(
color: Color(0xFF307645),
),
child: xx,
)
解决方法在as的Terminal里面
1.保证在下面根目录下执行下面:
flutter clean
2.然后cd到ios目录执行下面:
cd ios
3.最后执行这一步:
pod install
HomePageEntity homePageEntity =
HomePageEntity().fromJson(json.decode(testStr));
ListView.builder(
padding: EdgeInsets.only(top: 0),
);
GridView同理
onTap: (){
print("点击效果");
},
在Dart中万物皆对象,函数也是对象,用typedef定义两个函数类型,然后在HttpCallback声明这两个函数类型
typedef OnSuccess = void Function(Object o);
typedef OnError = void Function(Exception e);
class HttpCallback {
OnSuccess onSuccess;
OnError onError;
HttpCallback ({OnSuccess this.onSuccess, OnError this.onError});
}
然后在创建HttpCallback实例的时候可以传入这两个函数的具体实现
HttpClient.setCallBack(HttpCallback(
onSuccess: (Object o){
// 请求成功的处理
},
onError: (Exception e) {
// 请求失败的处理
},
));
get请求封装例子。唯一需要注意的是JSON解析,其它都是原生代码,JSON解析请看上方第14条。
import 'dart:convert';
import 'dart:io';
import 'home_page_entity.dart';
String url = "http://xxx";
typedef HomeDataListener = void Function(HomePageEntity homePageEntity);
class HomeDataManager {
factory HomeDataManager() => _getInstance();
static HomeDataManager get instance => _getInstance();
static HomeDataManager _instance;
HomeDataManager._internal() {
// 初始化
}
static HomeDataManager _getInstance() {
if (_instance == null) {
_instance = new HomeDataManager._internal();
}
return _instance;
}
HomePageEntity homePageEntity;
HomeDataListener _homeDataListener;
void init() {
getData(url).then((value) {
print("HomeDataManager value: " + value);
if (value != null) {
final valueJson = json.decode(value);
Map valueMap = valueJson;
print("HomeDataManager page: " + valueMap['data'].toString());
homePageEntity = HomePageEntity().fromJson(valueMap['data']);
if (_homeDataListener != null) {
_homeDataListener(homePageEntity);
}
}
});
}
void addDataListener(HomeDataListener homeDataListener) {
this._homeDataListener = homeDataListener;
}
void clearAll() {
homePageEntity = null;
_homeDataListener = null;
}
Future getData(String url) async {
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
request.headers.add("xxx", "xxx");
HttpClientResponse response = await request.close();
if (response.statusCode == HttpStatus.ok) {
String content = await response.transform(Utf8Decoder()).join();
httpClient.close();
return content;
}
return null;
}
Future postData(String url) async {
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
request.headers.add("xxx", "xxx");
// 添加请求参数
Map jsonMap = {'xxx': 'xxx'};
request.add(utf8.encode(json.encode(jsonMap)));
HttpClientResponse response = await request.close();
if (response.statusCode == HttpStatus.ok) {
String content = await response.transform(Utf8Decoder()).join();
httpClient.close();
return content;
}
return null;
}
}
doSyncMenthod("dataList").then((Object data){
String userName= data;
print(userName);
});
注意范型定义在方法名后面,而不是Java一样在修饰符后面。类上的定义则不变,都在类名后面。
T get(T t1, Object other) {
return t1;
}
class Test {
}
需要特别注意,我就被坑了一天的时间,心塞。
String messageStr = '{"cId":"1588923645858","cmd":2,"ct":"{\"createTag\":\"default_tag\",\"data\":\"{\\\"questionnaireId\\\":\\\"388cd47b01a74a0b994ed9dffa814b13\\\",\\\"stuName\\\":\\\"张三\\\",\\\"userId\\\":149160,\\\"index\\\":1}\",\"forward\":0,\"type\":40003}","from":"149160","gt":0,"id":"36478","rt":1,"tags":["ZTBSU5E1"]}';
通过通道传送给服务端变成了:
{"cId":"1588923645858","cmd":2,"ct":"{"createTag":"default_tag","data":"{\"questionnaireId\":\"388cd47b01a74a0b994ed9dffa814b13\",\"stuName\":\"张三\",\"userId\":149160,\"index\":1}","forward":0,"type":40003}","from":"149160","gt":0,"id":"36478","rt":1,"tags":["ZTBSU5E1"]}
转义符被摘了一层。
解决办法:字符前加上r,就表示原始字符串,转义符就不会被处理。原因应该是Flutter的字符串默认都是可以编辑的,比如在字符串里面加变量,不需要像Java一样主动format了。
正常List进行forEach会因为内部有await,所以加上async,导致forEach代码未执行就执行下方的代码,顺序就错了。所以需要使用 Future.forEach来保证同步。如下,是封装android权限的部分代码。
await Future.forEach(permissions, (permission) async {
bool isShow = await PermissionHandler()
.shouldShowRequestPermissionRationale(permission);
// 申请结果 权限检测
PermissionStatus permissionStatus =
await PermissionHandler().checkPermissionStatus(permission);
if (permissionStatus != PermissionStatus.granted) {
if (isShow) {
permissionUsefulList.add(permission);
}
}
});
dynamic出现时,不能使用范型代替,不然会报错:
如下:都为了获取int类型的数据,但开始使用了dynamic来表示,而后使用范型则报错。
Map messageData = json.decode(message);
getValue(messageData, "action");
/// 防止Map获取无字段而报错,拦截代码执行
static T getValue(Map map, String key) {
if (map != null && key != null && map.containsKey(key)) {
return map[key];
}
return null;
}
报错:
Another exception was thrown: type '_InternalLinkedHashMap' is not a subtype of type 'Map'
所以需要将T修改成dynamic。
当String为null时,日志输出会报错,所以即时是String,也要加上toString()。
class Foo { }
main() {
var foo = new Foo();
if (foo is Foo) {
print("it's a foo!");
}
}