配置文件存放:http_config.dart
const baseURL = "http://123.207.32.32:8000";
const timeout = 5000;
网络请求工具文件:http_request.dart
import'package:dio/dio.dart'; import'http_config.dart'; class HttpRequest { // 1.创建实例对象 static BaseOptions baseOptions = BaseOptions(connectTimeout: timeout); static Dio dio = Dio(baseOptions); static Future<T> request<T>(String url, {String method = "get",Map<String, dynamic> params}) async { // 1.单独相关的设置 Options options = Options(); options.method = method; // 2.发送网络请求 try { Response response = await dio.request<T>(url, queryParameters: params, options: options); return response.data; } on DioError catch (e) { throw e; } } }
这里使用API接口来请求数据:
https://douban.uieee.com/v2/movie/top250?start=0&count=20
模型对象的封装
在面向对象的开发中,数据请求下来并不会像前端那样直接使用,而是封装成模型对象:
前端开发者很容易没有面向对象的思维或者类型的思维。
但是目前前端开发正在向TypeScript发展,也在帮助我们强化这种思维方式。
为了方便之后使用请求下来的数据,将数据划分成了如下的模型:
首页数据请求封装以及模型转化
这里我封装了一个专门的类,用于请求首页的数据,这样让我们的请求代码更加规范的管理:HomeRequest
目前类中只有一个方法getMovieTopList;
后续有其他首页数据需要请求,就继续在这里封装请求的方法;
import'package:douban_app/models/home_model.dart'; import'http_request.dart'; class HomeRequest { Future<List<MovieItem>> getMovieTopList(int start, int count) async { // 1.拼接URL final url = "https://douban.uieee.com/v2/movie/top250?start=$start&count=$count"; // 2.发送请求 final result = await HttpRequest.request(url); // 3.转成模型对象 final subjects = result["subjects"]; List<MovieItem> movies = []; for (var sub in subjects) { movies.add(MovieItem.fromMap(sub)); } return movies; } }
在home.dart文件中请求数据
首页整体代码
首页整体布局非常简单,使用一个ListView即可
import'package:douban_app/models/home_model.dart'; import'package:douban_app/network/home_request.dart'; import'package:douban_app/views/home/childCpns/movie_list_item.dart'; import'package:flutter/material.dart'; const COUNT = 20; class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("首页"), ), body: Center( child: HomeContent(), ), ); } } class HomeContent extends StatefulWidget { @override _HomeContentState createState() => _HomeContentState(); } class _HomeContentState extends State<HomeContent> { // 初始化首页的网络请求对象 HomeRequest homeRequest = HomeRequest(); int _start = 0; List<MovieItem> movies = []; @override void initState() { super.initState(); // 请求电影列表数据 getMovieTopList(_start, COUNT); } void getMovieTopList(start, count) { homeRequest.getMovieTopList(start, count).then((result) { setState(() { movies.addAll(result); }); }); } @override Widget build(BuildContext context) { return ListView.builder( itemCount: movies.length, itemBuilder: (BuildContext context, int index) { return MovieListItem(movies[index]); } ); } }
下面是针对界面结构的分析:
import'package:douban_app/components/dash_line.dart'; import'package:flutter/material.dart'; import'package:douban_app/models/home_model.dart'; import'package:douban_app/components/star_rating.dart'; class MovieListItem extends StatelessWidget { final MovieItem movie; MovieListItem(this.movie); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(10), decoration: BoxDecoration( border: Border(bottom: BorderSide(width: 10, color: Color(0xffe2e2e2))) ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ // 1.电影排名 getMovieRankWidget(), SizedBox(height: 12), // 2.具体内容 getMovieContentWidget(), SizedBox(height: 12), // 3.电影简介 getMovieIntroduceWidget(), SizedBox(height: 12,) ], ), ); } // 电影排名 Widget getMovieRankWidget() { return Container( padding: EdgeInsets.fromLTRB(9, 4, 9, 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), color: Color.fromARGB(255, 238, 205, 144) ), child: Text( "No.$ {movie.rank}",//需要去除$ {中间空格 style: TextStyle(fontSize: 18, color: Color.fromARGB(255, 131, 95, 36)), ) ); } // 具体内容 Widget getMovieContentWidget() { return Container( height: 150, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ getContentImage(), getContentDesc(), getDashLine(), getContentWish() ], ), ); } Widget getContentImage() { return ClipRRect( borderRadius: BorderRadius.circular(5), child: Image.network(movie.imageURL) ); } Widget getContentDesc() { return Expanded( child: Container( padding: EdgeInsets.symmetric(horizontal: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ getTitleWidget(), SizedBox(height: 3,), getRatingWidget(), SizedBox(height: 3,), getInfoWidget() ], ), ), ); } Widget getDashLine() { return Container( width: 1, height: 100, child: DashedLine( axis: Axis.vertical, dashedHeight: 6, dashedWidth: .5, count: 12, ), ); } Widget getTitleWidget() { return Stack( children: <Widget>[ Icon(Icons.play_circle_outline, color: Colors.redAccent,), Text.rich( TextSpan( children: [ TextSpan( text: " " + movie.title, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold ) ), TextSpan( text: "($ {movie.playDate})",//需要去除$ {中间空格 style: TextStyle( fontSize: 18, color: Colors.black54 ), ) ] ), maxLines: 2, ), ], ); } Widget getRatingWidget() { return Row( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ StarRating(rating: movie.rating, size: 18,), SizedBox(width: 5), Text("$ {movie.rating}")//需要去除$ {中间空格 ], ); } Widget getInfoWidget() { // 1.获取种类字符串 final genres = movie.genres.join(" "); final director = movie.director.name; var castString = ""; for (final cast in movie.casts) { castString += cast.name + " "; } // 2.创建Widget return Text( "$genres / $director / $castString", maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 16), ); } Widget getContentWish() { return Container( width: 60, child: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ SizedBox(height: 20,), Image.asset("assets/images/home/wish.png", width: 30,), SizedBox(height: 5,), Text( "想看", style: TextStyle(fontSize: 16, color: Color.fromARGB(255, 235, 170, 60)), ) ], ), ); } // 电影简介(原生名称) Widget getMovieIntroduceWidget() { return Container( width: double.infinity, padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Color(0xfff2f2f2), borderRadius: BorderRadius.circular(5) ), child: Text(movie.originalTitle, style: TextStyle(fontSize: 18, color: Colors.black54),), ); } }