Flutter学习-14
Flutter第一个案例
 Flutter学习-14-MakerLi

 网络请求简单封装

配置文件存放: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]);
   }
  );
 }
}

单独Item局部

下面是针对界面结构的分析:

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),),
  );
 }
}