Flutter笔记: 获取网络数据及渲染列表

213 阅读3分钟

本篇文章记录我在使用Flutter开发中如何请求后端接口获取数据, 使用到的包有http用来发送请求,async提供Future抽象类以及convert用来将json数据转换为dart里面的对象。

首先使用flutter create xxx 命令或者 IDEA 新建一个Flutter项目,去掉示例代码,将需要的依赖引入

import 'dart:async';
import 'dart:convert';
 
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

根据接口返回数据定义好需要的数据类,例如接口返回的数据为一个数据列表

{
	"request_hash": "request:top:de22cb0aea0daa23a9e82809cf45d652ccce8ef3",
	"request_cached": false,
	"request_cache_expiry": 43200,
	"top": [{
		"mal_id": 32281,
		"rank": 1,
		"title": "Kimi no Na wa.",
		"url": "https:\/\/myanimelist.net\/anime\/32281\/Kimi_no_Na_wa",
		"image_url": "https:\/\/cdn.myanimelist.net\/images\/anime\/5\/87048.jpg?s=2bca128fcb9dfd6d0908f3d9986576c6",
		"type": "Movie",
		"episodes": 1,
		"airing_start": "Aug 2016",
		"airing_end": "Aug 2016",
		"members": 947231,
		"score": 9.14
	}, {
		"mal_id": 15335,
		"rank": 3,
		"title": "Gintama Movie 2: Kanketsu-hen - Yorozuya yo Eien Nare",
		"url": "https:\/\/myanimelist.net\/anime\/15335\/Gintama_Movie_2__Kanketsu-hen_-_Yorozuya_yo_Eien_Nare",
		"image_url": "https:\/\/cdn.myanimelist.net\/images\/anime\/10\/51723.jpg?s=27cd24446486572fb64c42a689d38902",
		"type": "Movie",
		"episodes": 1,
		"airing_start": "Jul 2013",
		"airing_end": "Jul 2013",
		"members": 121401,
		"score": 9.01
	}, {
		"mal_id": 34537,
		"rank": 50,
		"title": "Yoru wa Mijikashi Arukeyo Otome",
		"url": "https:\/\/myanimelist.net\/anime\/34537\/Yoru_wa_Mijikashi_Arukeyo_Otome",
		"image_url": "https:\/\/cdn.myanimelist.net\/images\/anime\/2\/86940.jpg?s=838b10398449d3354db5fb604b3b1b17",
		"type": "Movie",
		"episodes": 1,
		"airing_start": "Apr 2017",
		"airing_end": "Apr 2017",
		"members": 45656,
		"score": 8.32
	}]
}

根据需要的数据字段定义 Animate 类, Animate.fromJson 方法使用json数据生成一个Animate实例

class Animate {
  final int rank;
  final String imgUrl;
  final String title;
  final double score;
  final String url;
  final String airingStart;
  final String airingEnd;
 
  Animate({
    this.rank,
    this.imgUrl,
    this.title,
    this.score,
    this.url,
    this.airingStart,
    this.airingEnd,
  });
 
  factory Animate.fromJson(Map<String, dynamic> json) {
    return Animate(
      rank: json['rank'] as int,
      imgUrl: json['image_url'] as String,
      title: json['title'] as String,
      score: json['score'] as double,
      url: json['url'] as String,
      airingStart: json['airing_start'] as String,
      airingEnd: json['airing_end'] as String,
    );
  }
}

之后使用http发送请求,定义一个StatefulWidget以及它的State类,定义一个变量存储数据,定义获取数据的方法,然后重写类的initState方法,在initState方法里面调用请求数据的方法 16558c4a1f1d5724.png 拿到数据后就可以进行数据渲染了,定义一个StatelessWidget类来作为列表中的每一项,这个类需要一个Animate类的实例来填充数据 16558cdf59336823.png 最后使用ListView.builder方法生成ListView 16558d1b2e3f638e.png 完成效果如图 1655799f304e1a95.gif

完整代码

import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

//import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter get datas",
      theme: ThemeData(primarySwatch: Colors.deepPurple),
      debugShowCheckedModeBanner: false,
      home: AppHomePage(),
    );
  }
}

class Animate {
  final int rank;
  final String imgUrl;
  final String title;
  final double score;
  final String url;
  final String airingStart;
  final String airingEnd;

  Animate({
    this.rank,
    this.imgUrl,
    this.title,
    this.score,
    this.url,
    this.airingStart,
    this.airingEnd,
  });

  factory Animate.fromJson(Map<String, dynamic> json) {
    return Animate(
      rank: json['rank'] as int,
      imgUrl: json['image_url'] as String,
      title: json['title'] as String,
      score: json['score'] as double,
      url: json['url'] as String,
      airingStart: json['airing_start'] as String,
      airingEnd: json['airing_end'] as String,
    );
  }
}

class AnimateCard extends StatelessWidget {
  Animate animate;

  AnimateCard(this.animate);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: InkWell(
        onTap: () {
          print(animate.url);
        },
        child: ListTile(
          contentPadding: EdgeInsets.only(left: 10.0, right: 10.0),
          leading: Row(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.only(right: 6.0),
                child: Text(
                    animate.rank < 10 ? '0${animate.rank.toString()}' : animate.rank.toString()),
              ),
              Image(image: NetworkImage(animate.imgUrl), width: 45.0, height: 45.0),
            ],
          ),
          title: Padding(
            padding: EdgeInsets.only(bottom: 10.0),
            child: Text(
              animate.title,
              maxLines: 2,
              textAlign: TextAlign.start,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(color: Colors.deepPurple, fontSize: 16.0),
            ),
          ),
          subtitle: Row(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.only(right: 4.0),
                child: Text(
                  "上映日期:",
                  style: TextStyle(fontSize: 12.0),
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(right: 4.0),
                child: Text(
                  animate.airingStart,
                  style: TextStyle(fontSize: 12.0),
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(right: 4.0),
                child: Text(
                  "下映日期:",
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(fontSize: 12.0),
                ),
              ),
              Text(
                animate.airingEnd,
                style: TextStyle(fontSize: 12.0),
              ),
            ],
          ),
          trailing: Text(
            animate.score.toString(),
            style: TextStyle(
              fontWeight: FontWeight.bold,
              color: Colors.green,
            ),
          ),
        ),
      ),
    );
  }
}

class AppHomePage extends StatefulWidget {
  @override
  _AppHomePageState createState() => _AppHomePageState();
}

class _AppHomePageState extends State<AppHomePage> {
  List movies;

  Future getMovies({String type = 'anime', int page = 1, String subtype = 'movie'}) async {
    final String url = "https://api.jikan.moe/top/$type/$page/$subtype";
    final response = await http.get(url);

    if (response.statusCode == 200) {
      List top = json.decode(response.body)['top'];
      setState(() {
        movies = top.map((json) => Animate.fromJson(json)).toList();
      });
    } else {
      print("err code $response.statusCode");
    }
  }

  @override
  void initState() {
    super.initState();
    getMovies();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Top Animate Movies'),
      ),
      body: movies == null
          ? Center(child: CircularProgressIndicator())
          : Padding(
              padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 10.0),
              child: ListView.builder(
                itemCount: movies.length,
                itemBuilder: (BuildContext context, int index) {
                  return AnimateCard(movies[index]);
                },
              ),
            ),
    );
  }
}

获取不到数据记得Vpn翻墙

github地址

作者:轻剑快马 链接:juejin.im/post/5b7ac5… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。