1. 程式人生 > >Flutter實現網絡請求

Flutter實現網絡請求

list 直接 管理 後臺 hand form 依賴庫 part tel

  • Flutter網絡請求使用的是Dio。Dio是一個強大易用的dart http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載…

  • Flutter json數據解析是使用了json_serializable package包。它是一個自動化源代碼生成器,可以為我們生成JSON序列化模板。由於序列化代碼不再由我們手寫和維護,我們將運行時產生JSON序列化異常的風險降至最低。

  • Flutter網絡請求數據並且展示效果圖:
    技術分享圖片

  • 數據接口
    數據是使用的聚合數據的API,影訊API合集,大家可以註冊個賬號,申請使用一下,像這樣
    技術分享圖片

  • 添加依賴
    在pubspec.yaml文件中添加所需要的第三方依賴庫

     environment:
      sdk: ">=2.1.0 <3.0.0"
    
     dependencies:
       flutter:
         sdk: flutter
     
       json_annotation: ^2.0.0
       connectivity: ^0.4.2
       dio: ^2.0.15
       event_bus:  ^1.0.3
       # The following adds the Cupertino Icons font to your application.
       # Use with the CupertinoIcons class for iOS style icons.
       cupertino_icons: ^0.1.2
     
     dev_dependencies:
       flutter_test:
         sdk: flutter
    
  • 網絡請求過程分析

      	static netFetch(url,params,Map<String,String> header,Options option,{noTip = false}) async {
      	//獲取網絡的連接狀態,如果沒有連接網絡,返回NETWORK_ERROR
         var connectivityResult = await (new Connectivity().checkConnectivity());
         if(connectivityResult == ConnectivityResult.none) {
           return new ResultData(Code.errorHandleFunction(Code.NETWORK_ERROR, "", noTip),false,Code.NETWORK_ERROR);
         }
         //請求頭存放集合
         Map<String,String> headers = new HashMap();
         if(header != null) {
           headers.addAll(header);
         }
     	//option存放請求的一些配置信息
         if(option != null) {
           option.headers = headers;
         }else {
           //get請求
           option = new Options(method:‘get‘);
           option.headers = headers;
         }
         //超時時間15000ms
         option.connectTimeout = 15000;
     	//創建dio對象
         Dio dio = new Dio();
         Response response;
         try{
           //執行網絡請求,await和async配合使用,表示這是一個異步耗時操作
           //執行結果不會立馬返回。
           response = await dio.request(url,data: params,options: option);
         }on DioError catch(e) {
           //異常處理
           ....
           return new ResultData(Code.errorHandleFunction(errorResponse.statusCode, e.message, noTip), false, errorResponse.statusCode);
         }
         try{
             if(response.statusCode == 200 || response.statusCode == 201) {
               //網絡請求成功
               return await new ResultData(response.data, true, Code.SUCCESS,headers: response.headers);
           	 }
          }catch(e) {
     	  //異常處理
           print(‘返回參數‘ + e.toString() + url);
           return new ResultData(response.data, false, response.statusCode,headers: response.headers);
         }
         return new ResultData(Code.errorHandleFunction(response.statusCode, "", noTip), false, response.statusCode);
       }
    

    這個是最底層類封裝了一個靜態的請求方法,直接調用dio的request方法進行網路請求,很簡單。

       //如果這個是耗時方法,就會用async標記,耗時操作用await標記,表示是一個異步操作。
       static getTodayFilmListDao() async {
       	//獲取請求的url
         String url = Address.getTodayFilmList();
         //調用上面封裝的網絡請求方法進行網絡請求
         var res = await HttpManager.netFetch(url, null, null, null);
         if(res != null && res.result) {
           var data = res.data;
           if(data == null || data.length == 0) {
             return await new DataResult([],true);
           }
           //網絡請求成功,進行數據解析
           var response = TodayFilmResponse.fromJson(data);
           //返回數據
           return await new DataResult(response.result,true);
         }else {
           return await new DataResult(null,false);
         }
       }
    

    獲取url方法很簡單,就是字符串拼接了一下

      static getTodayFilmList() {
         return "${host}movie/movies.today?cityid=1&dtype=&key=713a408e855352d913806ef1e3ce3c33";
       }
    
  • 下面分析一下json數據解析過程。
    如上所說,json解析使用的是json_serializable package包。它是一個自動化源代碼生成器,可以為我們生成JSON序列化模板。
    網絡請求獲取到的json數據是這樣的

     		{
     			"movieId":"135808",
     			"movieName":"新喜劇之王",
     			"pic_url":"http:\/\/img5.mtime.cn\/mt\/2019\/02\/02\/113216.53857992_182X243X4.jpg"
     		},
    

    TodayFilmBean類

     	//TodayFilmBean.g.dart將在我們運行生成命令之後自動生成
     	part ‘TodayFilmBean.g.dart‘;
     	
     	//這個標註是告訴生成器,這個類是要生成的Model類
     	@JsonSerializable()
     	class TodayFilmBean{
     	  String movieId;
     	  String movieName;
     	  String pic_url;
     	  
     	   //構造函數
     	  TodayFilmBean(this.movieId,this.movieName,this.pic_url);
     	  
     	  //json轉換為bean對象
     	  factory TodayFilmBean.fromJson(Map<String,dynamic> json) => _$todayFilmBeanFromJson(json);
     	  
     	  //bean對象轉換為json
     	  Map<String,dynamic> toJson() => _$todayFilmBeanToJson(this);
     	}
    

    生成的TodayFilmBean.g.dart類是這樣的

     	part of ‘TodayFilmBean.dart‘;
     	
     	//json轉換為bean對象
     	TodayFilmBean _$todayFilmBeanFromJson(Map<String,dynamic> json) {
     	  return TodayFilmBean(json[‘movieId‘] as String,json[‘movieName‘] as String,
     	      json[‘pic_url‘] as String);
     	}
     	
     	//bean對象轉換為json
     	Map<String,dynamic> _$todayFilmBeanToJson(TodayFilmBean instance) =>
     	    <String,dynamic> {
     	      ‘movieId‘: instance.movieId,
     	      ‘movieName‘: instance.movieName,
     	      ‘pic_url‘:instance.pic_url
         };
    

    有兩種運行代碼生成器的方法:
    1.一次性生成
    通過在我們的項目根目錄下運行flutter packages pub run build_runner build,我們可以在需要時為我們的model生成json序列化代碼。這觸發了一次性構建,它通過我們的源文件,挑選相關的並為它們生成必要的序列化代碼。

    雖然這非常方便,但如果我們不需要每次在model類中進行更改時都要手動運行構建命令的話會更好。

    2.持續生成
    使用_watcher_可以使我們的源代碼生成的過程更加方便。它會監視我們項目中文件的變化,並且在需要時自動構建必要的文件。我們可以通過flutter packages pub run build_runner watch 在項目根目錄下運行來啟動_watcher_。

    只需啟動一次觀察器,然後並讓它在後臺運行,這是安全的。

    執行序列化只需執行

     //把json數據轉化為了bean對象
     var filmBean = TodayFilmBean.fromJson(json); 
    
  • 使用GridView最終展示結果

       DataResult dataResult;
       List<TodayFilmBean> mData = [];
       
       //當StatefulWiget被嵌入此view樹中,就會為此widget創建State對象
       //當State對象被創建了,frameWork就會調用initState()方法
       @override
       void initState() {
         //初始化數據
         getTodayFilm();
         super.initState();
       }
       
       
       void getTodayFilm() async {
         //這是一個異步操作,結果返回有一定延遲
         dataResult = await TodayDao.getTodayFilmListDao();
         //調用setState方法會通知framework控件狀態有變化,它會立馬觸發
         //State的build方法更新widget狀態
         setState(() {
           mData = dataResult.data;
         });
       }
    

    上面是初始化網絡請求,在請求到數據後,調用setState刷新UI

     	  //State的build方法,調用setState方法後,此方法就會被觸發
     	  //用來刷新UI
     	  @override
     	  Widget build(BuildContext context) {
     	
     	    return Scaffold(
     	      appBar: AppBar(
     	
     	        title: Text(widget.title),
     	      ),
     	      
     		  //如果mData.length == 0,展示一個loading框,否則展示數據
     	      body: mData.length == 0
     	          ? new Center(child: new CircularProgressIndicator()):
     	          //創建GridView對象
     	      	  new GridView.builder(
     	
     	          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
     	              crossAxisCount: 3, //每行2個
     	              mainAxisSpacing: 1.0, //主軸(豎直)方向間距
     	              crossAxisSpacing: 1.0, //縱軸(水平)方向間距
     	              childAspectRatio: 0.7 //縱軸縮放比例
     	          ),
     			  //item數量
     	          itemCount:mData.length,
    
     			  //創建每個item
     	          itemBuilder: (BuildContext context,int index) {
     	            return _getWidget(index);
     	          }),
     	    );
     	  }
    
       _getWidget(int index) {
         //添加要展示的item內容
           return new Column(
             children: <Widget>[
               new Expanded(child: new CardItem(color: Colors.black12,child: _getChild(index)),flex: 8,),
               //顯示網絡請求文本
               new Expanded(child: new Text(mData[index].movieName,
                 textAlign: TextAlign.end,
                 maxLines: 1,
               ),
                   flex:1)
             ]);
       }
     
       _getChild(int i) {
         return new Padding(padding: new EdgeInsets.all(1.0),
         	//顯示網絡請求的圖片
             child: new Image(image: NetworkImage(mData[i].pic_url)));
       }
    

    一個自定義的CardItem

     class CardItem extends StatelessWidget{
       final Widget child;
       final EdgeInsets margin;
       final Color color;
       final RoundedRectangleBorder shape;
       final double elevation;
     
       CardItem({@required this.color,this.child,this.elevation = 5.0,this.shape,this.margin});
     
       @override
       Widget build(BuildContext context) {
         EdgeInsets margin = this.margin;
         RoundedRectangleBorder shape = this.shape;
         Color color = this.color;
         margin ??= EdgeInsets.only(left: 2.0,top: 2.0,right: 2.0,bottom: 2.0);
         shape ??= new RoundedRectangleBorder(borderRadius: new BorderRadius.all(Radius.circular(4.0)));
         color ??= new Color(0xffeeff);
         return new Card(elevation: elevation,shape: shape,color: color,margin: margin,child: child,);
       }
     }
    

    好了,Flutter網絡請求並且展示數據就這樣實現的。
    最後附上demo地址:https://github.com/xinhuashi/flutter_http_demo.git

Flutter實現網絡請求