dart/flutter 学习笔记

flutter页面之间传值

调用代码:
        Navigator.push(context,
            new MaterialPageRoute(builder: (BuildContext context) {
              return GatewayPage(type: TTGatewayType.g2, wifi: wifiName);
            }));

传入页面:
class GatewayPage extends StatefulWidget {
  GatewayPage({required this.type, this.wifi}) : super();
  final String? wifi;
  final TTGatewayType type;
  @override
  _GatewayPageState createState() => _GatewayPageState(type, wifi);
}

class _GatewayPageState extends State<GatewayPage> {
  BuildContext? _context;
  String? _wifi;
  String? _wifiPassword;
  TTGatewayType? _type;
  _GatewayPageState(TTGatewayType type, String? wifi) {
    super.initState();
    _wifi = wifi;
    _type = type;
  }

 

map变量迭代

                                edit[selectedCommand]['fields'].forEach((key,  value)  {
                                  PARAM[key] = enItem[key];
                                });
                                edit[selectedCommand]['fields'].forEach((key,  value)  =>  PARAM[key]  =  enItem[key]);
                                PARAM..addAll(edit[selectedCommand]['fields'].map((key,  value)  =>  MapEntry(key,  enItem[key])));

 

慎重使用退出清除函数dispose,重置变量后容易忘记

  @override
  void dispose() {  // todo  清除页面树,退出页面时执行
    page = {};
    super.dispose();
  }

 

值拷贝

 //  列表值拷贝
    valuesByOriginalKey = List.from(values);

    //  Map的addEntries方法进行值拷贝
    Map<String,  dynamic>  copiedMap  =  Map.fromEntries(originalMap.entries);

 



类型转换
                              setState(() {
                                api(context, title, 'GET', url).then((res) {
                                  settings = (res?[resultKey] as List<dynamic>).cast<Map<String, dynamic>>();
                                  setState(() {}); // 在读取完值后调用setState更新UI
                                });
                              });

                        child: FindDropdown(
                          items: sMap['keys'].cast<String>(),

 



一、StreamBuildUtil

1、class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver {   // TODO 绑定 StreamBuildUtil
2、WidgetsBinding.instance.addObserver(this); // TODO 初始化 StreamBuildUtil.instance.getStream
3、定义控件
                      child: StreamBuildUtil.instance.getStream("password").addObserver((password) {
                        return Text(
                          '$password',
                          style: TextStyle(
                            color: Colors.red,
                            fontSize: 16.0,
                          ),
                        );
                      }, initialData: ''),

4、传值 StreamBuildUtil.instance.getStream("password").changeData(getFilterTitle());



函数参数传值

在 Dart 语言中,所有数据传递给函数时都是通过传递值。这意味着,当你把一个变量传递给函数时,你传递的是这个变量当前所持有的值。然而,基于数据类型的不同,这种 "传值" 的行为可能会给人 "传引用" 的错觉。

对于基本数据类型(如 intdoubleboolString),这些是不可变的值,因此看起来就像是 "传值"。对于复合数据类型(如 ListMap 和自定义的对象),虽然变量持有的是一个指向数据的引用,并且这个引用值被传递给函数,但从技术上讲,这仍然被视为 "传值",因为实际上传递的还是引用值本身,而不是对象。如果函数修改了传入的对象,那么外部的原始对象也会受到影响,因为它们共享同一个引用。

下面是一个示例,展示了传递不同类型数据给函数时的行为:

void main() {
  int myInt = 10;
  List<int> myIntList = [1, 2, 3];
  
  // 传递基本数据类型的值
  updateValue(myInt);
  print(myInt); // 输出: 10,因为基本数据类型在传递时本质上是拷贝值
  
  // 传递复合数据类型的引用
  updateList(myIntList);
  print(myIntList); // 输出: [4, 2, 3],因为引用类型在传递时是拷贝引用
}

void updateValue(int value) {
  value = 20; // 这里只是修改了函数内部的局部变量
}

void updateList(List<int> list) {
  list[0] = 4; // 这里修改了通过引用传递的列表的第一个元素
}

在上述代码中,函数 updateValue 尝试修改 myInt 变量,但这个修改仅限于函数内部的局部变量,外部的变量 myInt 不会被改变。另一方面,函数 updateList 修改了列表的第一个元素,由于 myIntList 是引用类型,所以这个修改也反映在了原始的列表对象上。

总结来说,不管是基本类型还是引用类型,在 Dart 中都是通过传递值来进行函数调用的。引用类型变量的值本质上是指向对象的引用本身,因此当这个引用值被传递给函数时,函数内对对象的操作会影响到原始对象。

在Flutter(以及Dart语言)中,double 类型同样没有 isEmpty 属性。正如我之前提及的,isEmpty 是一些集合类型和 String 类型的属性,它用来表示一个集合或字符串是否没有元素或字符。对于 double 这样的基本数值类型,它们总是包含一个数值,即使是 0.0,也不能认为是“空的”。

如果你尝试对一个 double 类型的变量使用 isEmpty 属性,你会得到一个编译时错误,因为这个属性对于 double 类型并不存在。

如果你要检查一个 int、double 是否未被赋值,你可以使用可空类型 double?,然后检查它是否为 null

double? number;
// 检查number是否为null
bool isNull = number == null;

这样你可以判断一个 double 类型的变量是否未初始化或者没有分配值。注意,这与 "empty" 是不同的概念,因为数值类型本身没有 "empty" 的状态。

Aqueduct


如果你想了解Aqueduct,先瞥一眼它的Core Concepts。我简单地列一下要点:

资源视角:Aqueduct是Restful 的忠实拥趸。所有的http请求都被视为一个资源请求。缺省的http content type 是Json格式。这和传统的web框架(比如SpringMVC或者Django)比,是不是省心了很多。

路由/控制器:Aqueduct通过显式编程模式,将url映射做了简单明了的处理。整个应该的所有请求都通过Entry Point里的路由定义实现。一个路由对应一个URL路径。而控制器包括资源控制器和路由中间件。中间件实现请求过滤,比如安全(认证/授权),日志,异常处理。而资源控制器实现服务请求的业务实现代码。

数据绑定:请求和返回值的数据序列话,如Json的编码/解码,都可以通过Bind标签自动完成。这虽然是很多web框架的标配,但是通过语言的标注绑定,还是实现的非常优雅,值得一试。

ORM:不仅能够将Dart的model class直接映射成数据库的表,而且还提供的数据迁移的版本控制。通过“aqueduct db generate”和“aqueduct db upgrade”,aqueduct可以自动识别每个版本之间的数据模型的差异,并自动生产升级/降级的脚本。

OAuth2.0:Auqeduct原生支持Oauth2.0框架,当然不喜欢的话,也可以自己通过中间件实现。但是,如果采用OAuth的话,几乎是不写一行代码的,拥有认证和授权能力,是不是很赞?

文档化:Auqeduct原生支持Open API 3.0(前身是Swagger Doc)。敲一句:aqueduct document,就可以得到标准的html格式的Open API文档,是不是很爽?



转换为特定类型

Map<String, Command> map = commandList[index].cast<String, Command>();
完成异步函数A、B、C,才能完成开始做D
  // 模拟异步函数A、B、C
  Future<void> asyncFunctionA() async {
    await Future.delayed(Duration(seconds: 1));
    print('异步函数A完成');
  }

  Future<void> asyncFunctionB() async {
    await Future.delayed(Duration(seconds: 2));
    print('异步函数B完成');
  }

  Future<void> asyncFunctionC() async {
    await Future.delayed(Duration(seconds: 3));
    print('异步函数C完成');
  }

  // 等待所有异步函数完成
  Future<void> waitForAsyncFunctions() async {
    await Future.wait([asyncFunctionA(), asyncFunctionB(), asyncFunctionC()]);
  }

 



 异步函数的同步初始化方法

  _load() {
    currentTest = GetStorage().read('test') as String;
    print('object');
    print(currentTest);
    setState(() {}); // 在读取完值后调用setState更新UI
  }

  @override
  void initState (){
    super.initState();
    Future.delayed(Duration.zero, () => setState(() {
      _load();
    }));

 




异步函数的阐释
@override
void initState (){
super.initState();
// todo 异步函数初始化方式1,注意:_center() async。
// todo _center是一个异步函数,它返回一个Future。当你调用then方法时,你需要传递一个接受单个参数的函数,
// todo 这个参数是_center函数异步执行完成后返回的结果。但是,在你的代码中,你传递的是一个不接受任何参数的匿名函数。
// todo 为了修复这个问题,你需要确保传递给then的函数接受一个参数。
// todo 注意,我在.then((_) {中添加了一个下划线_作为参数,这是Dart中一个常见的约定,表示这个参数在函数体内不会被使用。
// todo 同时,我也将companysName.length > 0替换为companysName.isNotEmpty,这是Dart中检查列表是否非空的更简洁的方式。
center(context).then((_) { // todo 添加不需要使用的返回参数(下划线 _ ),即使你不打算在函数体内使用它 ***center
setState(() {
companysNameState = companyNameList.isNotEmpty ? companyNameList : companysNameState;
locksNameState = lockNameList.isNotEmpty ? lockNameList : locksNameState;
// todo ************************************初始化列表后选择列表第一个为当前***********************************
currentCompany = companysNameState.length > 0 ? companysNameState[0] : currentCompany;
currentLock = locksNameState.length > 0 ? locksNameState[0] : currentLock;
});
});
// todo 异步函数初始化方式2,注意:_center() async
//Future.delayed(Duration.zero, () => setState(() {
//_center();
//}));
}

 

类型转换:
values = (data?['lock_list'] as List<dynamic>).cast<Map<String, dynamic>>();
解决下拉列表选择后显示正确选项问题:非空同时非null
String? selectedLock = (currentLock == null || currentLock!.isEmpty) ? '' : currentLock;
String? selectedCompany = (currentCompany == null || currentCompany!.isEmpty) ? '' : currentCompany; // todo 解决显示选择





在flutter中空字符串('')和空值(null)是不同概念

 



异步函数初始化中使用的2种方式

                        onPressed: ()  {
                          setState(() {
                            /// todo 假设_getCompanys是一个异步函数,用于获取公司名称列表
                            _center().then(() {
                              /// todo 确保在获取到数据后再更新companysNameState
                              setState(() {
                                companysNameState = companysName.length > 0 ? companysName : companysNameState;
                                locksNameState = locksName.length > 0 ? locksName : locksNameState;
                              });
                            });
                          });
                        },

 



flutter项目文件重点:

1、page文件:导航;
2、new、edit、fix文件:屏幕界面显示逻辑、变量定义、变量和界面元素即时刷新;
3、fn文件:界面相关的业务逻辑实现;
4、common文件:公用函数

数据即时更新和页面刷新:

1、要保证数据源更新后,控件立即更新,即数据源必须在在setState中更新;
2、在更新数据源时,注意要在异步函数执行完毕后进行。

 

 

这两段代码的主要区别在于"onFieldSubmitted"方法中是否调用了"setState"函数。

 

在第一段代码中,当用户在文本字段中提交内容时,"onFieldSubmitted"方法会调用"setState"函数,这将触发Flutter框架重新构建UI,以反映新的状态。在这种情况下,"TOKEN"的值被更新,并且如果"TOKEN"不为空,"labelTextCompany"的值将被清空,否则,它将被设置为'查找公司'。

 

在第二段代码中,"onFieldSubmitted"方法没有调用"setState"函数。这意味着即使"TOKEN"和"labelTextCompany"的值发生了变化,UI也不会立即更新以反映这些变化。只有当其他的"setState"被调用时,UI才会更新。

 

总的来说,如果你希望在用户提交文本字段内容后立即更新UI,你应该在"onFieldSubmitted"方法中调用"setState"函数。

 


flutter中数据传递的3种方式

1、通过全局变量赋值,用于一般情况;
2、需要实时更新的数据传递,例如收到mqtt时,同时保存到数据库的情况:
verificationCodeCtr.sink.add(iot1)
verificationCodeCtr.stream.listen((mobile) => isAuth(mobile));
3、处理完数据后,实时刷新到界面的情况
StreamBuildUtil.instance.getStream("text1").changeData('mqtt连接出错: $e');

StreamBuildUtil.instance.getStream("state").addObserver((state) {return Text("$now\n$state\n");

 
***通过控件中的setstate方法,改变最上层类的变量时,该变量赋值的控件属性会随之改变。***

 

 

flutter项目目录结构

 

对于最新权限修改、删除在 issue表 中有update_time字段标记,设备时间节点以update_time字段为准。

一、库

1、import 'dart:xxx';      引入Dart标准库
2、import 'xxx/xxx.dart';     引入绝对路径的Dart文件
3、import 'package:xxx/xxx.dart';         引入Pub仓库中的第三方库
4、import 'package:项目名/xxx.dart';            引入自定义的dart packages文件

5、例如有两个dart项目a和b  ,在同级目录下

,b需要引用a中的dart文件,则在b的pubspec.yaml文件中引用a的包

在b的dart文件中要引用a的dart文件则在B的dart文件中引入需要引入的文件

import 'package:a/aaaaaa.dart';

 

 











posted @ 2023-03-19 14:46  pearlcity  阅读(21)  评论(0编辑  收藏  举报