Flutter项目开发实战(AI助学)课件 6Flutter数据持久化与网络_第1页
Flutter项目开发实战(AI助学)课件 6Flutter数据持久化与网络_第2页
Flutter项目开发实战(AI助学)课件 6Flutter数据持久化与网络_第3页
Flutter项目开发实战(AI助学)课件 6Flutter数据持久化与网络_第4页
Flutter项目开发实战(AI助学)课件 6Flutter数据持久化与网络_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Flutter数据持久化与网络Flutter项目开发实战(AI助学)06Flutter+AI目录02数据持久化03网络请求01异步编程在介绍数据持久化和网络请求之前,介绍一下异步编程,因为数据持久化操作和网络请求操作都是异步‍的。6.1异步编程异步编程是处理耗时操作(如网络请求、文件读写、数据库操作)的核心机制。它允许应用在执行耗时任务时保持响应性,避免页面卡顿。在Flutter中,通常使用Future类结合async/await进行异步编程6.1.1Future介绍创建Future对象的方法较为简单,只需在方法返回类型前加Future关键字。例如,定义异步fetchData()方法的代码如‍下。//定义Future方法Future<String>fetchData(){return'操作结果';}创建Future01Future.delayed()方法表示等待一段时间再执行后面的代码,可用于模拟异步请求并等待返回结果。该方法接收两个参数:第一个为延迟时间,第二个为指定延迟时间到达后的回调函‍数。例如,用Future.delayed()方法设置等待2秒再输出操作结果,Duration()方法用来设置延迟时间,代码如‍下。Future<String>fetchData(){returnFuture.delayed(Duration(seconds:2),()=>'操作结果');}Future.delayed()026.1.1Future介绍Future.value()是一个静态方法,用于创建一个已经完成且带有指定值的Future对象。在需要模拟异步操作、返回缓存数据或简化代码逻辑时非常有用,语法如‍下。Future<T>value<T>(T?value)Future.value()03Future.error()是一个静态方法,用于创建一个已经失败且包含指定错误的Future对象。这在模拟异步操作失败以及需要处理异常或提前终止异步流程时非常有用,语法如‍下。Future<T>error(Objecterror,[StackTrace?stackTrace])Future.error()046.1.2async与await介绍async和await是Flutter中用于简化异步代码的语法糖。async用于标记一个函数是异步函数,会自动返回一个Future对象;await只能在async标记的函数中使用,用于暂停异步函数的执行,直到等待的Future完‍成。例如,在定义异步函数时使用async和await关键字,代码如‍下。//异步函数Future<String>fetchData()async{awaitFuture.delayed(Duration(seconds:2));return'操作结果';//操作结果在2秒后才会返回}6.1.3使用Future方法使用Future类定义异步函数后,调用时可通过then()方法获取返回结果,通过catchError()方法捕获错误信息,并在whenComplete()方法中执行后续代码,示例如‍下。//异步函数返回FutureFuture<String>fetchData()async{//模拟耗时操作awaitFuture.delayed(Duration(seconds:2));return'异步操作结果';}//使用Futurevoidmain(){print('开始执行');fetchData().then((result){print('获取结果:$result');}).catchError((error){print('发生错误:$error');}).whenComplete((){print('操作完成');});print('继续执行其他代码');}//输出顺序://开始执行//继续执行其他代码//(2秒后)//获取结果:异步操作结果//操作完成//异步函数Future<String>fetchData()async{//模拟耗时操作awaitFuture.delayed(Duration(seconds:2));return'异步操作结果';}//使用Futurevoidmainasync(){try{finalStringresult=awaitfetchData();print('获取结果:$result');}catch(error){print('发生错误:$error');}print('继续执行其他代码');}//输出顺序://开始执行//继续执行其他代码//(2秒后)//获取结果:异步操作结果//操作完成或目录02数据持久化03网络请求01异步编程数据持久化是指将应用中的数据存储在本地设备上,以便在应用重启后仍然可以访问这些数据。Flutter提供了3种数据持久化方案,包括shared_preferences(Flutter提供的一个插件)、文件操作和SQLite数据库,每种方案的适用场景不‍同。6.2数据持久化6.2.1shared_preferencesshared_preferences是Flutter中用于简单数据持久化的轻量级工具,就像日常生活中的便利贴,适合记录一些简单的信息。它以键值对的形式存储数据,底层依赖于Android的shared_preferences和iOS的NSUserDefaults,二者分别为各自平台原生的轻量级数据存储机‍制。shared_preferences支持存储的数据包括字符串(String)、整数(int)、布尔值(bool)、双精度浮点数(double)及字符串列表(List<String>)。可以用shared_preferences来保存用户的登录状态、应用的主题模式、音量大小等简单配置信‍息。6.2.1shared_preferences在使用shared_preferences之前需要先安装,首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“shared_preferences:^2.0.15”这行代码,表示安装2.0.15版本或更高版本的shared_preferences。dependencies:flutter:sdk:fluttershared_preferences:^2.0.15#版本号可根据实际情况更新安装shared_preferences01在VSCode中保存pubspec.yaml文件后会自动安装插件,如下图所示。安装成功后会在输出窗口中输出安装成功的提示,如下图所‍示。也可以打开VSCode的终端窗口,然后执行“flutterpubget”命令来手动安装插件,如下图所‍示。6.2.1shared_preferences图VSCode自动安装插件图插件安装成功图安装插件6.2.1shared_preferences安装完shared_preferences之后就可以使用它来存储、读取或删除数据了,使用前需要在对应的组件中导入插件,导入方法如‍下。import'package:shared_preferences/shared_preferences.dart';操作持久化数据0201:存储数据首先使用SharedPreferences.getInstance()方法初始化SharedPreferences实例,该操作为异步操作,需要用到await关键字,初始化完成后,可根据需要存储的数据的类型使用setXxx()方法来存储数据。02:读取数据读取数据之前也需要使用SharedPreferences.getInstance()方法初始化SharedPreferences实例,再使用与存储数据对应的getXxx()方法读取数据6.2.1shared_preferences存储数据,具体使用方法如‍下:import'package:shared_preferences/shared_preferences.dart';Future<void>saveData()async{//初始化SharedPreferences实例finalprefs=awaitSharedPreferences.getInstance();//保存字符串数据awaitprefs.setString('username','Alice');//保存整数awaitprefs.setInt('age',25);//保存布尔数据awaitprefs.setBool('isLoggedIn',true);//保存双精度浮点数awaitprefs.setDouble('height',1.65);//保存字符串列表数据awaitprefs.setStringList('hobbies',['reading','swimming']);}import'package:shared_preferences/shared_preferences.dart';Future<void>getData()async{finalprefs=awaitSharedPreferences.getInstance();//读取字符串数据,若不存在则返回默认值Stringusername=prefs.getString('username')??'Guest';//读取整数intage=prefs.getInt('age')??0;//读取布尔数据boolisLoggedIn=prefs.getBool('isLoggedIn')??false;//读取双精度浮点数doubleheight=prefs.getDouble('height')??0.0;//读取字符串列表数据List<String>hobbies=prefs.getStringList('hobbies')??[];print('Username:$username');print('Age:$age');print('IsLoggedIn:$isLoggedIn');print('Height:$height');print('Hobbies:$hobbies');}读取数据,具体使用方法如‍下:6.2.1shared_preferences删除数据前,也应通过SharedPreferences.getInstance()方法初始化实例,再调用remove()方法删除指定键的数据,或使用clear()方法清除全部数据,使用方法如‍下。删除与清空数据03import'package:shared_preferences/shared_preferences.dart';Future<void>removeAndClearData()async{finalprefs=awaitSharedPreferences.getInstance();//删除单个键值对awaitprefs.remove('username');//清空所有数据awaitprefs.clear();}6.2.2文件操作文件存储时需要访问当前的系统目录或文件目录,所以需要安装用于获取系统路径的Flutter官方插件path_provider。首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“path_provider:^2.1.1”这行代码,表示安装2.1.1版本或更高版本的path_provider。安装path_provider01dependencies:flutter:sdk:flutterpath_provider:^2.1.1#版本号可根据实际情况更新在VSCode中保存pubspec.yaml文件后会自动安装插件,也可以打开VSCode的终端窗口,然后执行“flutterpubget”命令来手动安装插‍件。写入文本文件读取文本文件删除文本文件列出目录下所有文件操作文本文件026.2.2文件操作写入文本文件,代码如‍下import'dart:io';import'package:path_provider/path_provider.dart';Future<void>writeTextFile(Stringcontent)async{//获取文档目录finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/my_file.txt');try{//写入内容(覆盖模式)awaitfile.writeAsString(content);print('文件写入成功');}catch(e){print('文件写入失败:$e');}}首先需要引入dart:io库和path_provider库,写入文本文件的过程是异步的,所以需要用Future类和async/await来定义方法。然后使用getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建文本文件对象file,最后调用file对象的writeAsString()方法将内容写入文件。6.2.2文件操作读取文本文件,代码如‍下Future<String>readTextFile()async{try{finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/my_file.txt');

//读取文件内容returnawaitfile.readAsString();}catch(e){print('文件读取失败:$e');return'';}}读取文本文件与写入文本文件的过程类似,整个过程也是异步的,需要用Future类和async/await来定义方法。然后通过getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建文本文件对象file,最后调用file对象的readAsString()方法读取文件内容6.2.2文件操作删除文本文件,代码如‍下Future<String>deleteTextFile()async{try{finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/my_file.txt');//删除文件内容if(awaitfile.exists()){awaitfile.delete();}return'';}catch(e){print('文件删除失败:$e');return'';}}删除文本文件的过程与读取、写入文本文件的过程类似,也是异步的,需要用Future类和async/await来定义方法。然后通过getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建文本文件对象file,最后调用file对象的exists()方法判断当前文件是否存在,如果存在就调用file对象的delete()方法删除文件6.2.2文件操作列出目录下所有文件,代码如‍下Future<List<FileSystemEntity>>listFiles()async{finaldirectory=awaitgetApplicationDocumentsDirectory();returndirectory.listSync();}列出所有文件的过程也是异步的,需要用Future类和async/await来定义方法。然后通过getApplicationDocumentsDirectory()方法获取文件目录对象directory,接着调用directory对象的listSync()方法列出当前目录下的所有文件操作JSON数据是常见的本地数据持久化需求,适用于保存配置信息、设置用户偏好、缓存数据等场景。操作JSON数据分为序列化与反序列化、写入JSON文件、读取JSON文件3个过程:操作JSON数据03序列化与反序列化写入JSON文件读取JSON文件6.2.2文件操作序列化与反序列化是处理JSON数据的核心环节,序列化是指将内存中的对象转换为JSON字符串的过程,反序列化是指将JSON字符串转换为内存中对象的过‍程。在Flutter中一般使用dart:convert库来处理JSON数据,其中jsonEncode()方法用于将Dart对象序列化为JSON格式的字符串,jsonDecode()方法用于将JSON格式的字符串反序列化为Dart数据结‍构。例如,定义一个User类,包含name和age两个属性,以及toJson()和fromJson()两个方法,代码如‍下。import'dart:convert';classUser{finalStringname;finalintage;User({required,requiredthis.age});//对象转JSONMap<String,dynamic>toJson()=>{'name':name,'age':age,};//JSON转对象factoryUser.fromJson(Map<String,dynamic>json)=>User(name:json['name'],age:json['age']);}6.2.2文件操作写入JSON文件的过程是异步的,所以需要用Future类和async/await来定义方法。然后使用getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建JSON文件对象file,通过jsonEncode()方法将user数据转换为字符串,最后调用file对象的writeAsString()方法将内容写入文件。代码如‍下。Future<void>saveJsonData(Useruser)async{finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/user.json');//将对象转换为JSON字符串finaljsonString=jsonEncode(user.toJson());//写入文件awaitfile.writeAsString(jsonString);}6.2.2文件操作写入JSON文件6.2.2文件操作读取JSON文件与写入JSON文件的过程类似,整个过程也是异步的,需要用Future类和async/await来定义方法。然后通过getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建JSON文件对象file,再调用file对象的readAsString()方法读取文件内容,最后通过jsonDecode()方法将user数据解析为JSON对象。代码如‍下。Future<User?>loadJsonData()async{try{finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/user.json');

//读取文件内容finaljsonString=awaitfile.readAsString();//解析为JSON对象finaljsonMap=jsonDecode(jsonString);returnUser.fromJson(jsonMap);}catch(e){print('JSON读取失败:$e');returnnull;}}6.2.2文件操作存储二进制文件(如图片、音频、视频、PDF文件等)是常见的需求,尤其是在需要离线缓存或本地处理媒体资源的应用中。直接写入本地文件:写入二进制文件的过程是异步的,所以需要用Future类和async/await来定义方法。然后使用getApplicationDocumentsDirectory()方法获取文件目录,接着用File类的构造函数创建二进制文件对象file,最后通过writeAsBytes()方法将二进制数据写入文件。存储二进制文件04dependencies:flutter:sdk:flutterhttp:^1.4.0#版本号可根据实际情况更新从网络下载后保存:从网络下载文件需要安装http插件。首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“http:^1.4.0”这行代码,表示安装1.4.0版本或更高版本的http插‍件。Future<void>saveImage(Uint8ListimageBytes)async{finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/profile.jpg');awaitfile.writeAsBytes(imageBytes);

//写入二进制数据}6.2.2文件操作从网络下载文件的过程是异步的,因此需要使用Future类和async/await来定义方法,随后调用http库的get()方法根据指定地址下载文件。import'package:http/http.dart'ashttp;Future<void>downloadAndSaveImage(Stringurl)async{try{//1.发起网络请求finalresponse=awaithttp.get(Uri.parse(url));//2.检查响应状态if(response.statusCode!=200){throwException('图片下载失败,状态码:${response.statusCode}');}//3.获取存储目录finaldirectory=awaitgetApplicationDocumentsDirectory();finalfile=File('${directory.path}/$fileName');

//4.写入图片数据awaitfile.writeAsBytes(response.bodyBytes);returnfile;}catch(e){print('下载图片时出错:$e');rethrow;}}6.2.3SQLite数据库当需要存储和管理结构化数据且数据量较大时,shared_preferences的功能受限,此时可采用SQLite数据库进行数据管理。SQLite是一种轻量级的嵌入式数据库引擎,常通过如sqflite等插件在Flutter中使用,用于高效存储和管理结构化数据。它类似于一个电子账本,能够有序地记录和管理各类复杂信息。SQLite适用于存储如用户详细信息、商品订单数据、聊天记录等复杂数据,它支持完整的SQL语句操作,提供了强大的数据管理功‍能。01安装sqflite插件在使用SQLite之前需要安装sqflite插件,首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“sqfliter:^2.3.0”这行代码,表示安装2.3.0版本或更高版本的sqflite。另外需添加path库的依赖,以便于路径管‍理。02初始化数据库SQLite是轻量级的数据库,在使用之前需要初始化数据库。首先引入sqflite和path库,并定义initDatabase()方法用于初始化数据‍库。由于数据库的操作都是异步的,所以需要用Future类和async/await来定义方法。然后使用join()方法和getDatabasesPath()方法定义数据库的路径和数据库名称,最后调用openDatabase()方法来打开数据库,传入路径、版本和表结构等信‍息。dependencies:flutter:sdk:fluttersqflite:^2.3.0 #版本号可根据实际情况更新path:^1.8.3 #路径管理6.2.3SQLite数据库使用db.execute()方法创建users表,该表包含id、name和age字段,其数据类型分别为INTEGER、TEXT和INTEGER,用于后续数据存储,代码如‍下。import'package:sqflite/sqflite.dart';import'package:path/path.dart';//初始化数据库Future<Database>initDatabase()async{//获取数据库文件路径finalStringpath=join(awaitgetDatabasesPath(),'my_database.db');returnopenDatabase(path,version:1,onCreate:(db,version)async{//创建表awaitdb.execute('''CREATETABLEusers(//PRIMARYKEY表示主键,是唯一不重复的,AUTOINCREMENT表示自动增长idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,ageINTEGER)''');},);}6.2.3SQLite数据库03数据操作插入数据:定义insertUser()方法插入用户数据。插入数据的过程是异步的,需要用Future类和async/await来定义方法。查询数据:查询数据的过程也是异步的,需要用Future类和async/await来定义方法。更新数据:更新数据的过程也是异步的,需要用Future类和async/await来定义方法。删除数据:删除数据与查询数据的过程类似,也是异步的,需要用Future类和async/await来定义方法。04事务处理事务处理是确保数据一致性和完整性的重要机制,尤其适用于需要执行多个关联操作的场景(如转账、批量插入等)。原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失‍败。一致性(Consistency):事务执行前后,数据库状态保持一‍致。隔离性(Isolation):多个事务并发执行时,相互之间不会产生干‍扰。持久性(Durability):事务提交后,数据变更永久保‍存。6.2.3SQLite数据库插入数据,代码如‍下//插入数据Future<void>insertUser(Stringname,intage)async{finalDatabasedb=awaitinitDatabase();awaitdb.insert('users',{'name':name,'age':age},//插入数据时如果发生冲突,则用新数据代替旧数据conflictAlgorithm:ConflictAlgorithm.replace,);}//使用示例finalnewUser={'name':'JohnDoe','age':30,'email':'john@'};finaluserId=awaitinsertUser(newUser);首先调用initDatabase()方法初始化数据库并获取数据库实例,接着调用db.insert()方法插入数据,分别传入表名(users)、具体字段的数据(name和age),以及conflictAlgorithm属性(插入数据发生冲突时的策略)。6.2.3SQLite数据库查询数据,代码如‍下//查询数据Future<List<Map<String,dynamic>>>queryAllUsers()async{finalDatabasedb=awaitinitDatabase();returnawaitdb.query('users');}//使用示例finalusers=awaitqueryAllUsers();print('所有用户:$users');首先调用initDatabase()方法初始化数据库并获取数据库实例,接着调用db.query()方法并传入表名(users)参数以查询数据。更新数据,代码如‍下//更新数据Future<int>updateUser(Map<String,dynamic>user)async{finalDatabasedb=awaitinitDatabase();returnawaitdb.update('users',user,where:'id=?',whereArgs:[user['id']],);}//使用示例finalupdatedUser={'id':1,'age':31,//更新年龄};finalrowsAffected=awaitupdateUser(updatedUser);首先调用initDatabase()方法初始化数据库并获取数据库实例,接着调用db.update()方法并传入表名(users)、要更新的数据(user)、where和whereArgs参数以更新对应id的数据。6.2.3SQLite数据库删除数据,代码如‍下Future<int>deleteUser(intid)async{finalDatabasedb=awaitinitDatabase();returnawaitdb.delete('users',where:'id=?',whereArgs:[id],);}//使用示例finaldeletedRows=awaitdeleteUser(1);首先调用initDatabase()方法初始化数据库并获取数据库实例,接着调用db.delete()方法并传入表名(users)、where和whereArgs参数6.2.3SQLite数据库05封装类操作数据库时通常会单独封装一个类,将所有的增、删、改、查操作在类中进行定义,结构更为清晰,也有利于代码的复用与维护。例如,可将数据库的初始化、连接/关闭及用户表的相关操作封装在UserDatabase类中,其他模块在操作数据库时可直接调用该类的方法,提升了代码的可读性与可维护性,代码如‍下。classUserDatabase{staticfinalUserDatabaseinstance=UserDatabase._init();staticDatabase?_database;UserDatabase._init();Future<Database>getdatabaseasync{if(_database!=null)return_database!;_database=await_initDB('users.db');return_database!;}Future<Database>_initDB(StringfilePath)async{finaldbPath=awaitgetDatabasesPath();finalpath=join(dbPath,filePath);returnawaitopenDatabase(path,version:1,onCreate:_createDB);}Future_createDB(Databasedb,intversion)async{awaitdb.execute('''CREATETABLEusers(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,ageINTEGER,emailTEXTUNIQUENOTNULL)''');}6.2.3SQLite数据库//插入用户Future<int>createUser(Useruser)async{finaldb=awaitinstance.database;returnawaitdb.insert('users',user.toMap());}//查询所有用户Future<List<User>>readAllUsers()async{finaldb=awaitinstance.database;finalresult=awaitdb.query('users');returnresult.map((map)=>User.fromMap(map)).toList();}//更新用户Future<int>updateUser(Useruser)async{finaldb=awaitinstance.database;returnawaitdb.update('users',user.toMap(),where:'id=?',whereArgs:[user.id],);}//删除用户Future<int>deleteUser(intid)async{finaldb=awaitinstance.database;returnawaitdb.delete('users',where:'id=?',whereArgs:[id],);}//关闭数据库Futureclose()async{finaldb=awaitinstance.database;db.close();}}//删除用户Future<int>deleteUser(intid)async{finaldb=awaitinstance.database;returnawaitdb.delete('users',where:'id=?',whereArgs:[id],);}//关闭数据库Futureclose()async{finaldb=awaitinstance.database;db.close();}}目录02数据持久化03网络请求01异步编程在Flutter应用开发中,网络请求是与后端服务交互的核心功能,用于获取数据、提交表单、上传/下载文件等。网络请求包含HTTP请求与WebSocket请求等。在Flutter中,http请求插件和dio请求插件是常用的HTTP请求方式,web_socket_channel请求插件是常用的WebSocket请求方‍式。6.3网络请求6.3.1HTTP请求HTTP请求是客户端(如浏览器、App)向服务器发送的一种数据请求消息,用于获取资源、提交数据或执行特定操作。GET请求GET请求一般用于获取数据,参数加在URL后面,其长度受限于浏览器和服务器对URL长度的限制,通常适用于查询列表、查看详情数据等场‍景。POST请求POST请求常用于提交数据,参数放在请求体中,无长度限制,常用于表单提交、创建资源和文件上传等场‍景。0102http是Dart中用于发起HTTP请求的插件,使用它可以实现与服务器的数据交‍互。http插件的安装方法为,首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“http:^1.1.0”这行代码,表示安装1.1.0版本或更高版本的http请求插‍件。6.3.2http请求插件dependencies:flutter:sdk:flutterhttp:^1.1.0#最新稳定版本在VSCode中保存pubspec.yaml文件后会自动安装插件。也可以打开VSCode的终端窗口,然后执行“flutterpubget”命令来手动安装插‍件。6.3.2http请求插件首先引入http请求插件,由于请求的过程是异步的,所以需要用async/await来定义异步方法。然后使用http.get()方法请求数据,将链接作为参数传入。接着判断返回结果的statusCode是不是200,如果是200,代表请求成功,将body数据解析后返回;如果不是200,则抛出异常,代码如‍下。GET请求用法01import'package:http/http.dart'ashttp;import'dart:convert';Future<Map<String,dynamic>>fetchData()async{finalresponse=awaithttp.get(Uri.parse(''));if(response.statusCode==200){//解析JSONreturnjsonDecode(response.body);}else{throwException('请求失败:${response.statusCode}');}}6.2.2文件操作使用http.post()方法请求数据,参数包含链接、请求头和请求体,请求头设置为'application/json',请求体中用jsonEncode()方法将参数转义。接着判断返回结果的statusCode是不是200,如果是200,代表请求成功,将body数据解析后返回;如果不是200,则抛出异常,代码如‍下。POST请求用法02Future<Map<String,dynamic>>postData(Map<String,dynamic>data)async{finalresponse=awaithttp.post(Uri.parse('t'),headers:{'Content-Type':'application/json'},body:jsonEncode(data),);if(response.statusCode==201){returnjsonDecode(response.body);}else{throwException('提交失败:${response.statusCode}');}}dio也是一个功能强大的用于发起HTTP请求的插件,相比http,它提供了更简洁、易用的API,并且支持更多高级功能,如请求拦截、响应拦截、表单数据上传‍等。dio在使用之前需要安装。首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“dio:^5.3.2”这行代码,表示安装5.3.2版本或更高版本的dio。6.3.3dio请求插件dependencies:dio:^5.3.2首先需要引入dio库,引入后使用Dio()方法初始化,初始化时可传入相关的配置,如设置请求的baseUrl为“

”,那么后续请求的链接路径都会以这个路径为主;设置connectTimeout为5000,表示请求连接的超时时间为5s;设置receiveTimeout为3000,表示接收超时时间为3s;headers中可设置请求头的相关参数,代码如右侧:初始化dio01import'package:dio/dio.dart';finaldio=Dio(BaseOptions(baseUrl:'',connectTimeout:5000,//连接超时时间receiveTimeout:3000,//接收超时时间headers:{'Content-Type':'application/json','Accept':'application/json',},));6.3.3dio请求插件使用dio.get()方法请求数据,将请求地址的相对路径作为参数传入,在queryParameters字段中传入请求的参数,接着返回接口返回的数据,代码如‍下。发起GET请求02Future<List<dynamic>>fetchProducts()async{try{finalresponse=awaitdio.get('/products',queryParameters:{'category':'electronics','limit':10,});

returnresponse.data;}catch(e){print('请求异常:$e');rethrow;}}6.3.3dio请求插件使用dio.post()方法请求数据,将请求地址的相对路径作为参数传入,在data字段中传入请求的参数,请求参数可使用FormData.fromMap()方法转换格式,接着返回接口返回的数据,代码如‍下。发起POST请求03Future<dynamic>submitForm(Stringusername,Stringemail)async{finalformData=FormData.fromMap({'username':username,'email':email,});finalresponse=awaitdio.post('/register',data:formData);returnresponse.data;}文件上传的过程也是异步的,所以需要用Future类和async/await来定义方法;然后使用dio.post()方法请求数据,将请求地址的相对路径作为参数传入,在data字段中传入请求的参数,请求参数可使用FormData.fromMap()方法转换格式,传入的文件使用MultipartFile.fromFile()方法进一步转换格式;接着返回接口返回的数据,代码如‍下。文件上传04Future<dynamic>uploadFile(StringfilePath)async{finalformData=FormData.fromMap({'file':awaitMultipartFile.fromFile(filePath),});finalresponse=awaitdio.post('/upload',data:formData);returnresponse.data;}6.3.3dio请求插件拦截器(Interceptor)是一种特殊的机制,用于在HTTP请求发送前或响应返回后对数据进行拦截和处理。它类似于中间件,允许开发者在不修改核心业务逻辑的情况下统一处理请求头、日志记录、错误‍等。拦截器一般分为请求拦截、响应拦截和错误处理几个部分,使用dio插件的每个请求都会进入拦截器。拦截器05import'package:dio/dio.dart';//创建Dio实例finaldio=Dio(BaseOptions(baseUrl:'',connectTimeout:5000,receiveTimeout:3000,));//添加拦截器erceptors.add(InterceptorsWrapper(//请求拦截onRequest:(options,handler){print('请求拦截:${options.uri}');

//添加认证Tokenoptions.headers['Authorization']='Beareryour_token';returnhandler.next(options);

//继续处理请求

//若想终止请求,可使用://returnhandler.reject(DioException(requestOptions:options));},

//响应拦截onResponse:(response,handler){print('响应拦截:${response.statusCode}');//统一处理响应数据if(response.dataisMap&&response.data.containsKey('code')){if(response.data['code']==200){

returnhandler.next(response); //业务成功}else{//业务失败returnhandler.reject(DioException(requestOptions:response.requestOptions,error:response.data['message'],),);}}6.3.3dio请求插件returnhandler.next(response);},

//错误拦截onError:(DioExceptione,handler){print('错误拦截:${e.message}');

//统一处理网络错误switch(e.type){caseDioExceptionType.connectionTimeout:print('连接超时');break;caseDioExceptionType.receiveTimeout:print('接收超时');break;caseDioExceptionType.sendTimeout:print('发送超时');break;caseDioExceptionType.badResponse:print('响应错误:${e.response?.statusCode}');break;default:print('其他错误:${e.error}');}//继续传递错误returnhandler.next(e);},));6.3.4使用WebSocket请求import'dart:io';voidmain()async{//1.建立WebSocket连接finalwebSocket=awaitWebSocket.connect('ws:///socket');

//2.监听消息事件webSocket.listen((message){//处理接收到的消息if(messageisList<int>){

print('Receivedbinarydata:${message.length}bytes');//二进制消息}else{

print('Receivedtext:$message');//文本消息}},onDone:(){print('WebSocket连接已关闭');},onError:(error){print('WebSocket错误:$error');},01WebSocket介绍Flutter内置了WebSocket支持,借助dart:io库可以直接创建WebSocket连接。使用WebSocket.connect()方法创建一个WebSocket连接,然后通过webSocket.listen()方法监听消息,其中onDone()方法用于监听关闭连接,onError()方法用于监听发生错误消息,webSocket.add()方法用于发送新消息,webSocket.close()方法用于关闭WebSocket连接,代码如‍下。onOpen:(){print('WebSocket连接已打开');//连接成功后发送消息webSocket.add('Hello,WebSocket!');},);//3.发送消息(示例:5s后发送)Future.delayed(Duration(seconds:5)).then((_){webSocket.add('这是一条延迟发送的消息');});//4.关闭连接(示例:10s后关闭)Future.delayed(Duration(seconds:10)).then((_){webSocket.close();});}6.3.4使用WebSocket请求dependencies:flutter:sdk:flutterweb_socket_channel:^2.4.0#版本号可根据实际情况更新02使用web_socket_channel封装库web_socket_channel是一个用于实现WebSocket通信的核心库,它提供了简单而强大的API,支持双向实时数据传‍输。web_socket_channel库在使用之前需要安装。首先打开项目根目录下的pubspec.yaml文件,并在dependencies下添加“web_socket_channel:^2.4.0”这行代码,表示安装2.4.0版本或更高版本的web_socket_channel。在VSCode中保存pubspec.yaml文件后会自动安装插件。也可以打开VSCode的终端窗口,然后执行“flutterpubget”命令来手动安装插‍件。与WebSocket原生用法类似,使用IOWebSocketChannel.connect()方法建立连接,使用channel.stream.listen()方法监听消息,其中onError()方法用于监听错误消息,onDone()方法用于监听关闭连接,channel.sink.add()方法用于发送新消息,channel.sink.close()方法用于关闭连接,代码如下:6.3.4使用WebSocket请求import'package:web_socket_channel/io.dart';import'package:web_socket_channel/web_socket_channel.dart';voidmain(){//1.创建WebSocketChannelfinalchannel=IOWebSocketChannel.connect('ws:///socket');

//2.监听消息流channel.stream.listen((message){print('接收到消息:$message');},onError:(error){print('错误:$error');},onDone:(){print('连接已关闭');},);//3.发送消息channel.sink.add('初始化消息');

//4.关闭连接channel.sink.close();}

本单元动手工坊主要任务是完成商品详情和收货地址管理页面的搭建,主要分为3个任务,分别是搭建商品详情页面布局、完善购买流程和搭建收货地址管理页面布‍局。1.搭建商品详情页面布局从首页或分类页面点击商品信息时会跳转到商品详情页面,包含商品图片、商品价格,以及商品介绍等信息,页面支持上下滑动查看商品介绍。页面底部包含购物车图标和“立即购买”按钮,如图所示。点击购物车图标会跳转到购物车页面,点击“立即购买”按钮会进入购买流‍程。图商品详情页面1图

商品详情页面2【动手工坊】仿京东App—搭建商品详情和收货地址管理页面仿京东App开发精讲

点击底部的“立即购买”按钮时会打开“立即购买”弹窗,包含商品图片、商品标题、商品单价、商品规格(默认选中第一个规格)和商品数量(数量支持增加或减少)等信息,弹窗底部有“加入购物车”和“立即购买”按钮,如图所‍示。点击“加入购物车”按钮,系统会将该商品以选中的规格和数量加入购物车;点击“立即购买”按钮,会弹出一个包含商品图片、商品单价、商品规格及已选数量的“确认订单”弹窗,并自动计算商品总价,如图所示。用户还需填写收货地址(已有地址直接显示,如果没有则显示“请选择收货地址”,如图所示),点击收货地址或“请选择收货地址”时,将跳转至选择收货地址页面,如图所示。选择完成后,所选地址将被回填至“确认订单”弹窗。点击“立即支付”按钮,系统将模拟支付成功,并跳转至订单列表页面,生成一笔新订单。2.完善购买流程【动手工坊】仿京东App—搭建商品详情和收货地址管理页面图

“立即购买”弹窗图

“确认订单”弹窗(有默认收货地址)图

“确认订单”弹窗(没有默认收货地址)图

选择收货地址页面【动手工坊】仿京东App—搭建商品详情和收货地址管理页面商品详情页面文件(位置:商业案例/jd_mall_app/lib/pages/product_detail_page.dart)代码如下所‍示。//商品详情页面文件import'package:flutter/material.dart';import'package:provider/provider.dart';import'../model/cart_item.dart';import'../provider/cart_provider.dart';import'cart_page.dart';import'../provider/order_provider.dart';//AddedimportforOrderProviderimport'address_manage_page.dart';import'../provider/user_provider.dart';import'../model/user.dart';//商品详情页面组件//展示商品详细信息,支持规格选择、加入购物车和立即购买classJDProductDetailPageextendsStatefulWidget{//要展示的商品信息finalCartItemproduct;constJDProductDetailPage({super.key,requiredduct});@overrideState<JDProductDetailPage>createState()=>_JDProductDetailPageState();}class_JDProductDetailPageStateextendsState<JDProductDetailPage>{//选中的商品数量int_selectedCount=1;//选中的商品规格String?_selectedSpec;//选中的收货地址String?_selectedAddress;//显示规格选择底部弹窗//用于选择商品规格和数量void_showSpecBottomSheet(){finalspecs=duct.specs??['默认'];//默认选中第一个规格if(_selectedSpec==null&&specs.isNotEmpty){_selectedSpec=specs[0];}

//商品“立即购买”底部弹窗showModalBottomSheet(context:context,isScrollControlled:true,shape:constRoundedRectangleBorder(borderRadius:BorderRadius.vertical(top:Radius.circular(18)),),【动手工坊】仿京东App—搭建商品详情和收货地址管理页面builder:(context){returnPadding(padding:MediaQuery.of(context).viewInsets,child:StatefulBuilder(builder:(context,setModalState){returnPadding(padding:constEdgeInsets.all(16.0),child:Column(mainAxisSize:MainAxisSize.min,crossAxisAlignment:CrossAxisAlignment.st

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论