云起工作室 15711107967
03_flutter安装配置

1、安装配置JDKhttps://www.oracle.com/java/technologies/downloads/#java8-windows1.1|双击安装1.2、配置环境变量 JAVA_HOME D:\tools\java\jdk-1.8将 %JAVA_HOME%\bin 放到path 里将%JAVA_HOME%\jre\bin 放到path里windows cmd 打开命令窗口 输入 java 或javac 有内容说明安装成功2、安装android studio可以跳过proxy 设置安装完成之后再 plugin 里安装flutter 插件3、电脑上下载配置Flutter Sdkhttps://docs.flutter.dev/release/archive?tab=windows下载解压将解压目录/flutter/bin 目录配置到 path 环境变量查看windows cmd flutter --version4、电脑上配置flutter 国内镜像 非必须将 PUB_HOSTED_URL = https://pub.flutter-io.cnFLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn配置到环境变量查看cmd 里输入:flutter doctor 将android sdk 的 platform-tools 和 tools 路径配置到 环境变量运行flutter doctor 命令检测环境是否配置成功,根据提示配置安装对应的软件4.1、报错 Android toolchain - develop for Android devicescmdline-tools component is missing打开android studio ->SDK Manager->Android SDK-> SDK Tools-> Android SDK Command-line Tools(latest) ok5、打开android studio 安装flutter 插件6、创建运行flutter 项目选择flutter 的sdk目录 注意不是 bin目录 是flutter的安装目录创建号项目,重新打开项目选择android项 打开后选择file-> sync project width gridle files 失败的话 其实就是在下载gradle下载失败的话,就将项目中的 gradle-wrapper.properties中 distributionUrl 的值换成国内的 ,如:distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.5-all.zip

10_网络请求

http 和diohttp加载http包官网使 pub.dev网络请求有两个 一个使dio 一个是httphttp包地址:https://pub.dev/packages/http在pubspec.yml 文件里 输入在dependencies:包含的最下方http: ^1.2.1点击pub get使用import 'package:http/http.dart' as http定义网络方法getDatas() async { final url=Uri.parse("接口地址"); final response = await http.get(url); //print(response.statusCode) //200 print(response.body)}使用getDatas()或者child: FutureBuilder( future: getDatas(), builder: (BuildContext context, AsyncSnapshot snapshot) { print("dddd"); print(snapshot); if(snapshot.connectionState == ConnectionState.waiting){ return Text("loading"); } return Container(); },),异步请求使用定义Future<List> getList() async{ final url = Uri("/api/porjectlist"); final response = await http.get(url); if(response.statusCode==200){ return json.decode(response.body)['list']; }else{ throw Exception("errorcode"); }}使用getList().then((val){})dio加载在pubspec.yaml里dependencies下 dio:^4.0.1使用import “package:dio/dio.dart”final dio = Dio();dio.get("api/projectlist").then((val){}) get请求dio.download("下载地址","保存路径/文件名称").then((value){}); 下载第二个参数可以是路径的字符串,也可以是回调函数手机端路径获取String path = Dorectory.systemTemp.path+"/文件名称";dio.download(url,(header){ return path},onReveiveProgress:showDownloadProgress) .whenComplete((){}).catchError((e){})void showDownloadProgress(int count,int total){}

12_异步/线程

事件队列【异步处理】Future() 相当于 new Promise()Future.await() 相当于 new Promise.all()getData() async{ await Future((){ 执行逻辑 }) print("dd");}Future((){ return "";}).then((value){}),catchError((e){})多链式异步Future.await([ Future((){return ""}), Future((){return ""}), Future((){return ""}),]).then((val){}).catchError((err){})微任务 队列比Future先执行scheduleMicrotask((){})sleepsleep(Duration(seconds:10))多线程Isolate.spawnvoid test(){ print("1"); Isolate.spawn(func,10); sleep(Duration(seconds:2)); print("2");}func(int count){ print("3");}执行顺序为 1,3,2线程里的方法不会影响到外部变量,如:var a= "10";void test(){ print("1"); Isolate.spawn(func,10); sleep(Duration(seconds:2)); print(a); //输出 10 而不是100}func(int count){ a="100"; print("3");}如何让线程里的方法影响到外部变量var a= "10";void test() async { print("1"); ReceivePort port = ReceivePort(); Isolate iso =await Isolate.spawn(func,port.sendPort); port.listen((message){ a=message; port.close(); iso.kill(); }) sleep(Duration(seconds:2)); print(a); }func(SendPort port){ a="100"; send.send("100");}compute 是 Isolate.spawn的简化版,不需要再kill并且可以直接使用返回值,不需要再借用ReceivePort定时循环int count = 0;Timer.periodic(Duration(seconds:2),(timer){ count ++; print(count); if(count==99){ timer.cancel(); //终止定时循环 }})在页面销毁timer场景:在页面设置保持状态的情况下,如果来回切换页面,会创造多个timer 造成内存泄漏class _ChatPage extends State<ChatPage>{ Timer _timer; void dispose(){ if(_timer!=null && _timer.isActive){ _timer.cancel(); } super.dispose(); } void initState(){ _timer = Timer.periodic(Duration(seconds:2),(timer){ //处理逻辑 timer.cancel() }) }}

05_flutter组件

void main(){ runApp(App());}class App extends StatelessWidget{ Widget build(BuildContext context){ return MaterialApp( home:Home() ) )}class Home extends StatelessWidget{ Widget _itemForRow(BuildContext context,int index){ //return Text(datas[index].name!); 显示文字 //显示图片 return Container( color:Colors.white, margin:const EdeInsets.all(10), //child:Image.network(datas[index].imageUrl) child:Column( children:<Widget>[ Image.network(datas[index].imageUrl!), Text("hello") ] ) ) } Widget build(BuildContext context){ return Scaffold( //app 的顶部 appBar:AppBar( title:const Text("app name") ), body:ListView.builder( itemBuilder:_itemForRow, itemCount:datas.length, ) ) }}final List<Car> datas=[ const Car( name:"保时捷", imageUrl:"https://img.path.com/img.jpg" )]class Car{ Car(this.name,this.imageUrl); final string name!; final string imageUrl!;}组件汇总MeterialAppScaffoldAppBar 属性 centerTitle:true 使文本居中 child:PopupMenuButton 导航里的列表选项 索引位actions child:Image(),点击的按钮 itemBuilder:(BuildContext context){ //点击显示的导航列表 return <PopupMenuItem<String>>[ PopupMenuItem() ] }CenterContainer Stack 悬浮控件 父元素, 子元素都堆叠再一起 内容区域大小取决于第一个元素大小Positioned 悬浮空间 与stack 结合使用才有效ListView ListView直接用需要设置高度,否则可能报错,但高度不确定,所以用 Expanded 包一下也可解决报错问题RichTextTextSpanTextRow 其属性 mainAxisAlignment: MainAxisAlignment.* 相当于display:flexCloumnIconAspectRatio 设置宽高比 属性是 aspectRatioChip 气泡FloatingActionButton 悬浮按钮ElevatedButton 普通按钮SizeBox() 可设置宽高,占位用BottomNavigationBar 底部导航栏 属性 items 有4个以上元素时 要设置 type:BottomNavigationBarType.fixedBottomNavigationBarItem 底部导航栏元素Image icon:Image(image:AssetImage('images/test.png'))Image.asset()Expanded //撑满剩余部分MediaQuery.of(context).size.width 获取设备宽度MediaQuery.of(context).size.height 获取设备高度MediaQuery.removePadding() //通过MediaQuery.removePadding可以移除元素的pandding,需要注意要指定移除哪个方向的padding,例如移除上面的padding MediaQuery.removePadding( removeTop: true, context: context, child: , )BoxDecoration 背景设置decoration:BoxDecoration( borderRadius:BorderRadius.circular(5.0), 设置圆角 image:DecorationImage())ListTile // 左图,右上标题右下内容 组件 ListTile( title:Text("内容标题"), subtitle:Container(), // 简介 leading:CircleAvatar( //头像 backgroundImage:Network() ) )GestureDetector( onTap:(){}) //监听点击组件Center( child:Text( "text", textDirection:TextDirection.rtl ))设置底部导航颜色Scanffold( bottonNavigationBar:BottomNavigationBar( onTap:(index){ //点击事件 setState((){}) }, selectedFontSize:12.0, //被选中时的字体大小 type:BottomNavigationBarType.fixed, fixedColor:Colors.green, 被选中时的颜色 currentIndex:1 //当前被选中的索引 items:[] ))底部导航点击会有水波纹效果 ,如何取消在MaterialApp 里设置主题 高亮透明即可MaterialApp( theme:ThemeData( highLightColor:Color.fromRGBO(0,0,0,0.0), //取消点击是的水波纹 splashColor:Color.fromRGBO(0,0,0,0.0), //取消弹开时的水波纹 primarySwatch:Colors.blue, //设置主题颜色 ))去掉appBar的阴影条AppBar( elevation:0.0)树结构MterialApp home:Scaffold appBar body

06_状态管理

无状态组件 StatelessWidget有状态组件 StatefulWidgetvoid main(){ runApp(StateManage());}class StateManage extends StatefulWidget{ @override State<StatefulWidget> createState(){ return _SMDState(); }}状态管理class _SMDState extends State<StateManage>{ int count = 0; @override Widget build(BuildContext context) { // TODO: implement build return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text("app bar title"), ), body: Center( child: Chip( label:Text("$count") ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: (){ setState(() { //更新状态 count++; }); }, ), ), ); }}使用外部传来的参数用widget. 来访问 StatefulWidget 中的成员class StateManage extends StatefulWidget{ final String title; final String name; StateManage({ @required this.title, //@required 是必传的意思 this.name }) : assert(title!=null,"title不能为空") @override State<StatefulWidget> createState(){ return _SMDState(); }}状态管理class _SMDState extends State<StateManage>{ @override Widget build(BuildContext context) { return Text(widget.title); //通过widget. 可以使用传过来的参数 }}不同页面共享数据class MyData extends InheritedWidget{ final int data; const MyData({required this.data,require Widget child}):super(child:child); //定义一个便捷方法,方便子组件中得widget去获取共享得数据 ?是空安全 static MyData? of(BuildContext context){ return context.dependOnInheritedWidgetOfExactType(); } bool updateShouldNotify(MyData oldWidget){ return oldWidget.data!=data; }}使用Text(MyData.of(context)!.data.toString())


09_知识点

删除顶部的paddingbody:Container( child:MediaQuery.removePadding( removeTop:true, context:context, child: 再些子组件 ))监听事件GestureDetector( onVerticalDragUpdate:(DragUpdateDetails details){ //监听拖拽事件 RenderBox box = context.findRenderObject(); 拿到点前小部件的盒子 print(box.globalToLocal(details.globalePosition)) ; //获取offset 相对坐标 double y= box.globalToLocal(details.globalePosition).dy 获取相对高度 } onTap:(){}, //监听点击事件 child:组件) 比较排序var arr = [a,b,c,t,a,y,s]arr.sort((val1,val2){ return val1.compareTo(val2)})相除取整int dividend = 10; // 被除数 int divisor = 3; // 除数 int result = dividend ~/ divisor; // 结果为3 print(result); // 输出: 3限制取值范围dividend ~/ divisor.clamp(最小值,最大值)定义回调函数class IndexBar extends StatefulWidget{ final void Function(string str) indexBarCallBack; IndexBar({this.indexBarCallBack})}外部使用IndexBar(indexBarCallBack:(string str){ print(str)})widget.indexBarCallBack(str)不要再有拖拽事件的组件里用ListView 因为ListView 自带拖拽事件,会拦截自定义的拖拽事件返回页面Navigator.pop(context)是否包含某字符串name.contains("str") // 判断name中是否包含某字符串name.split(str) 返回数组 以str 切割name数据共享定义class MyData extends InheritedWidget{ final int data; const MyData({required this.data,required Widget child}):puper(context); //定义一个便捷方法,方便子组件中的widget去获取共享数据 static MyData? of(BuildContext context){ return context.dependOnInheritedWidgetOfExactType<MyData>(); } bool updateShouldNotify(MyData oldWidget){ return oldWidget.data!=data }}使用Widget build(BuildContext context){ return MyData(data:data,child:child)}Widget build(BuildContext context){ return Text(MyData.of(context)!.data.toString());}