Flutter学习记录

前言

最近因工作需要,重温Flutter。2018年初次尝试Flutter后写过从入门到放弃,今年重新学习过后,觉得Flutter已经可以创作想要的App了。

目前对于Flutter出现的问题,Google上基本都可以找到答案了,整体生态链也趋向成熟,唯一不太肯定的是对于Flutter的三方架包的支持,但实在不行也可以使用混合开发解决。

今天就记录下零零碎碎的知识点,针对大些的内容,后面单独写博客记录。

正题

1. 图片使用

     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')

2. 设置屏幕为横屏

初始设置待定,可能需要通过修改主题实现

强制屏幕为横屏

SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);

强制屏幕为竖屏

SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);

3. 图片显示,对应android ScaleType

首先在Image使用时,可以直接使用fit来显示,但这个效果有限,一般都需要在外层套一个容器布局,设定大小,同时也可以再加上Align对齐方式,进行综合布局来设置图片的显示形式,达到Android ImageView的scaleType效果

child: Image.asset(
    "assets/imgs/xxx.png",
    fit: BoxFit.fitWidth,
),

4. Flutter尺寸单位与适配

获取屏幕宽高,Flutter的屏幕适配,使用screenWidth就可以了,不必较真真实的屏幕像素大小,而且在IOS上,计算出来的都是错误的。

screenWidth = MediaQuery.of(context).size.width as int;
screenHeight = MediaQuery.of(context).size.height as int;

5. Flutter的日志输出

print("test");

6. 设置Text

Text(
    "hello flutter!",
    textAlign:TextAlign.center,
    maxLines:1,
    overflow:TextOverflow.ellipsis, // 显示不完,就在后面显示点点
    style:TextStyle(
        fontSize:30.0, // 文字大小
        color:Colors.yellow, // 文字颜色
    ),
),

7. 颜色的使用

color: const Color(0xFF0099ff)

8. Flutter 控件对齐(类似RelativeLayout)

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,

9. 设置全屏幕显示

  只隐藏顶部状态栏

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([]);

10. Flutter延迟执行

  第一种,缺点是无法取消

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

11. Flutter页面跳转

// 跳转不关闭当前页面
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage()));
// 跳转并关闭当前页面
Navigator.pushAndRemoveUntil(
    context,
    new MaterialPageRoute(builder: (context) => new HomePage()),
    (route) => route == null,
);

12. 设置背景颜色

Container(
    decoration: BoxDecoration(
        color: Color(0xFF307645),
    ),
    child: xx,
)

13. 出现Debug.xcconfig line 1: Unable to find included file “Pods/Target Support Files/Pods错误

解决方法在as的Terminal里面 
1.保证在下面根目录下执行下面: 
   flutter clean 
2.然后cd到ios目录执行下面: 
   cd ios 
3.最后执行这一步: 
   pod install

14. 使用JSON

  1. 使用插件FlutterJsonBeanFactory
  2. 文件夹右键,New --> JsonToDartBeanAction
  3. 解析使用如下:
HomePageEntity homePageEntity =
HomePageEntity().fromJson(json.decode(testStr));

15. Flutter listView 顶部有距离,无法置顶

ListView.builder(
    padding: EdgeInsets.only(top: 0),
);

 GridView同理

16. 匿名内部类写法

onTap: (){
    print("点击效果");
},

17. 定义监听回调

在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) {
        // 请求失败的处理
    },
));

18. 如果构造里面有成员变量的获取设置,则初始化时设置的值会失效。如果没有传入,则成员变量会变成null。

19. 网络请求

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<String, dynamic> 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<String> 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<String> 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;
    }
}

20. 异步方法返回后,转到UI线程

doSyncMenthod("dataList").then((Object data){
    String userName= data;
    print(userName);
});

21. Flutter范型定义

注意范型定义在方法名后面,而不是Java一样在修饰符后面。类上的定义则不变,都在类名后面。

T get<T>(T t1, Object other) {
    return t1;
}

class Test<T> {
}

22. 转义字符

需要特别注意,我就被坑了一天的时间,心塞。

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了。

23. forEach同步问题

正常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);
        }
    }
});

24. dynamic和范型T的注意点

dynamic出现时,不能使用范型代替,不然会报错: 
如下:都为了获取int类型的数据,但开始使用了dynamic来表示,而后使用范型则报错。

Map<String, dynamic> messageData = json.decode(message);
getValue(messageData, "action");

/// 防止Map获取无字段而报错,拦截代码执行
static T getValue(Map<String, T> map, String key) {
    if (map != null && key != null && map.containsKey(key)) {
        return map[key];
    }
    return null;
}

报错:

Another exception was thrown: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Map<String, int>'

所以需要将T修改成dynamic。

25. print时特别注意String

当String为null时,日志输出会报错,所以即时是String,也要加上toString()。

26. 判断对象类型

class Foo { }

main() {
    var foo = new Foo();
    if (foo is Foo) {
        print("it's a foo!");
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值