使用天行数据网提供的新闻接口实现一个新闻推送小项目

前言

天行数据网虽然不是免费提供数据接口调用服务,但对于新注册的用户,会提供10000次的免费接口调用额度,对于具体申请的某类接口提供了100次的免费调用额度。所以要使用天行数据网提供的接口服务,首先要注册成为天行数据网的用户,然后申请某种数据接口服务。


一、注册天行网申请接口?

链接: link.点击进入天行网官网注册,并找到自己感兴趣的接口,申请即可。本文选用接口如下。

Xcod flutter ios项目结构 flutter小项目_List

二、在flutter中使用http请求插件,并获取json,转化为LIST

1.引入库

Xcod flutter ios项目结构 flutter小项目_flutter_02

Xcod flutter ios项目结构 flutter小项目_json_03


代码如下(示例):

Flutter中使用http请求获取json使用异步方法

代码如下

Future<List<CommonModel>> getDatas() async {
    final response = await http.get(
        'http://api.tianapi.com/generalnews/index?key=7ffbc5561c177f7d61b9c440ec0c6975&num=50&page=1');
    Utf8Decoder decode = new Utf8Decoder();
    Map<String, dynamic> result = jsonDecode(decode.convert(response.bodyBytes));
    //print(result);
    List<CommonModel> datas; //转列表
    datas = result['newslist'].map<CommonModel>((item) => CommonModel.fromJson(item)).toList();
    return datas; //返回 初始化
  }

2.建立新闻类

代码如下(示例):

此处可以根据天行网说明建立类的相关属性

Xcod flutter ios项目结构 flutter小项目_android_04

class CommonModel {
  int code;//状态编码
  String msg;//状态信息
  String ctime;//时间
  String title;//标题
  String description;//再看
  String picUrl;//来源
  String url;//url

  CommonModel({this.code,this.msg,this.ctime, this.title, this.description,this.picUrl,this.url});

  factory CommonModel.fromJson(Map<String, dynamic> json) {
    return CommonModel(
        code: json['code'],
        msg: json['msg'],
        ctime: json['ctime'],
        title: json['title'],
        description: json['description'],
        picUrl: json['picUrl'],
        url: json['url']);
  }
}

3.获取了换回的json后就只需要构建布局即可,

Widget build(BuildContext context) {
    var theme = Constants.currentTheme == Constants.dayTheme
        ? ThemeData(
        brightness: Brightness.light,
        primaryColor: Colors.blue,
        accentColor: Colors.orangeAccent)
        : ThemeData(
        brightness: Brightness.dark,
        primaryColor: Colors.black,
        accentColor: Colors.cyan);

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: theme,
        home: Scaffold(
          appBar: AppBar(
            leading: Icon(Icons.title),
            title: Text('新闻推送', style: TextStyle(color: Colors.white)),
            actions: <Widget>[
              IconButton(
                icon: Icon(Icons.assignment),
                onPressed: (() {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => AboutPage()),
                  );
                }),
              ),
              IconButton(
                  icon: Icon(Icons.autorenew),
                  onPressed: (() {
                    Constants.eventBus.fire(//发送消息给main构建app
                        Constants.currentTheme == Constants.dayTheme ? ThemeEvent(Constants.nightTheme) : ThemeEvent(Constants.dayTheme));
                  }))
            ],
          ),
          body: Container(
            child: ListView.builder(
              itemCount: _datas.length,
              itemBuilder: (BuildContext context, int index) {
                  return Card(
                  color: Colors.grey[250],
                  elevation: 5.0,
                    child: InkWell(
                      child: new Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                        Image.network(_datas[index].picUrl, fit: BoxFit.fitWidth),
                         Padding(
                              padding: const EdgeInsets.all(10.0),
                              child: Text(
                                _datas[index].title,
                              style: TextStyle(
                              fontSize: 16.0,
                              fontWeight: FontWeight.bold,
                              ),
                              ),
                         ),
                          Padding(
                            padding: _datas[index].description.isEmpty
                            ? const EdgeInsets.all(0.0)
                              : const EdgeInsets.only(
                            left: 10.0, right: 10.0, bottom: 10.0),
                            child: Text(
                              _datas[index].description,
                            style: TextStyle(
                            fontSize: 12.0,
                            ),
                            ),
                           ),
                          Padding(
                           padding: const EdgeInsets.only(left: 10.0, right: 10.0),
                            child: Text(
                          '时间:${_datas[index].ctime}',
                          style: TextStyle(
                          fontSize: 12.0,
                          ),
                          ),
                          ),
                          ],
                          ),
                          onTap: () {
                          _onItemClick(index, _datas[index]);
                          },
                          ),
                  );

              },
            ),
          ),
        )
    );
  }

3.新闻详情页面

在我们的新闻卡片列表中设置一个点击时间,点击后调用函数,进入新闻详情页面的新路由即可。
处理点击事件的函数

//新闻详情
  _onItemClick(int position, CommonModel data) {
    if (data.url == null || data.url.isEmpty) {
      Scaffold.of(context).showSnackBar(SnackBar(
        content: new Text('缺少新闻链接'),
        duration: Duration(seconds: 1),
      ));
    } else {
      Navigator.of(context).push(MaterialPageRoute(
          builder: (ctx) => NewsDetailPage(
            url: data.url,
            title:data.title,
          )));
    }
  }

新闻详情页面使用webwiew插件,实现在自己的项目中,根据url请求网页

Xcod flutter ios项目结构 flutter小项目_android_05

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
class NewsDetailPage extends StatefulWidget {
  final String url;
  final String title;

  const NewsDetailPage({Key key, this.url, this.title})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => NewsDetailPageState();
}

class NewsDetailPageState extends State<NewsDetailPage> {
  bool loaded = false;
  String detailDataStr;
  final flutterWebViewPlugin = FlutterWebviewPlugin();

  NewsDetailPageState({Key key});

  @override
  void initState() {
    super.initState();
    flutterWebViewPlugin.onStateChanged.listen((state) {
      print("state: ${state.type}");
      if (state.type == WebViewState.finishLoad) {
        setState(() {
          loaded = true;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {

    List<Widget> titleContent = [];
    titleContent.add(Text(
      widget.title,
      overflow: TextOverflow.ellipsis,
      style: TextStyle(
          fontSize: 13.0
      ),
      )
    );
    if (!loaded) {
      titleContent.add(CupertinoActivityIndicator());
    }
    titleContent.add(Container(width: 50.0));
    return WebviewScaffold(
      url: widget.url,
      appBar: AppBar(
        title: Expanded(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: titleContent,
          ),
        )
      ),
      withZoom: false,
      withLocalStorage: true,
      withJavascript: true,
    );
  }
}

总结

项目总体结构

Xcod flutter ios项目结构 flutter小项目_json_06


http_news.dart

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import'NewsDetailPage.dart';
import 'AboutPage.dart';
import 'Constants.dart';
class qimo extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<qimo> {
  int lenth;
  String ctime;
  String title;
  String description;
  String picUrl;
  String url;
  List<CommonModel> _datas = [];
  bool _cancelConnect = false; //多次请求!

  @override
  void initState() {
    super.initState();
    Constants.eventBus.on<ThemeEvent>().listen((event) {
      setState(() {
        //TODO 应该添加至SP中持久化,待下次进入时使用
        Constants.currentTheme = event.themeModel;
      });
    });
    getDatas().then((List<CommonModel> datas) {
      if(!_cancelConnect){
        setState(() {
          _datas = datas; //初始化state 重新刷新页面
        });
      }
    }).catchError((e) {
      print('error$e');
    }).whenComplete((){
      print('完毕');
    }).timeout(Duration(seconds: 1)).catchError((timeOut){
      //超时:TimeoutException after 0:00:00.010000: Future not completed
      print('超时:${timeOut}');
      _cancelConnect = true;
    });//设置超时时间
  }
  //get请求
  Future<List<CommonModel>> getDatas() async {
    final response = await http.get(
        'http://api.tianapi.com/generalnews/index?key=7ffbc5561c177f7d61b9c440ec0c6975&num=50&page=1');
    Utf8Decoder decode = new Utf8Decoder();
    Map<String, dynamic> result = jsonDecode(decode.convert(response.bodyBytes));
    //print(result);
    List<CommonModel> datas; //转列表
    datas = result['newslist'].map<CommonModel>((item) => CommonModel.fromJson(item)).toList();
    return datas; //返回 初始化
  }

  //新闻详情
  _onItemClick(int position, CommonModel data) {
    if (data.url == null || data.url.isEmpty) {
      Scaffold.of(context).showSnackBar(SnackBar(
        content: new Text('缺少新闻链接'),
        duration: Duration(seconds: 1),
      ));
    } else {
      Navigator.of(context).push(MaterialPageRoute(
          builder: (ctx) => NewsDetailPage(
            url: data.url,
            title:data.title,
          )));
    }
  }


  @override
  Widget build(BuildContext context) {
    var theme = Constants.currentTheme == Constants.dayTheme
        ? ThemeData(
        brightness: Brightness.light,
        primaryColor: Colors.blue,
        accentColor: Colors.orangeAccent)
        : ThemeData(
        brightness: Brightness.dark,
        primaryColor: Colors.black,
        accentColor: Colors.cyan);

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: theme,
        home: Scaffold(
          appBar: AppBar(
            leading: Icon(Icons.title),
            title: Text('新闻推送', style: TextStyle(color: Colors.white)),
            actions: <Widget>[
              IconButton(
                icon: Icon(Icons.assignment),
                onPressed: (() {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) => AboutPage()),
                  );
                }),
              ),
              IconButton(
                  icon: Icon(Icons.autorenew),
                  onPressed: (() {
                    Constants.eventBus.fire(//发送消息给main构建app
                        Constants.currentTheme == Constants.dayTheme ? ThemeEvent(Constants.nightTheme) : ThemeEvent(Constants.dayTheme));
                  }))
            ],
          ),
          body: Container(
            child: ListView.builder(
              itemCount: _datas.length,
              itemBuilder: (BuildContext context, int index) {
                  return Card(
                  color: Colors.grey[250],
                  elevation: 5.0,
                    child: InkWell(
                      child: new Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                        Image.network(_datas[index].picUrl, fit: BoxFit.fitWidth),
                         Padding(
                              padding: const EdgeInsets.all(10.0),
                              child: Text(
                                _datas[index].title,
                              style: TextStyle(
                              fontSize: 16.0,
                              fontWeight: FontWeight.bold,
                              ),
                              ),
                         ),
                          Padding(
                            padding: _datas[index].description.isEmpty
                            ? const EdgeInsets.all(0.0)
                              : const EdgeInsets.only(
                            left: 10.0, right: 10.0, bottom: 10.0),
                            child: Text(
                              _datas[index].description,
                            style: TextStyle(
                            fontSize: 12.0,
                            ),
                            ),
                           ),
                          Padding(
                           padding: const EdgeInsets.only(left: 10.0, right: 10.0),
                            child: Text(
                          '时间:${_datas[index].ctime}',
                          style: TextStyle(
                          fontSize: 12.0,
                          ),
                          ),
                          ),
                          ],
                          ),
                          onTap: () {
                          _onItemClick(index, _datas[index]);
                          },
                          ),
                  );

              },
            ),
          ),
        )
    );
  }
}

class CommonModel {
  int code;//状态编码
  String msg;//状态信息
  String ctime;//时间
  String title;//标题
  String description;//再看
  String picUrl;//来源
  String url;//url

  CommonModel({this.code,this.msg,this.ctime, this.title, this.description,this.picUrl,this.url});

  factory CommonModel.fromJson(Map<String, dynamic> json) {
    return CommonModel(
        code: json['code'],
        msg: json['msg'],
        ctime: json['ctime'],
        title: json['title'],
        description: json['description'],
        picUrl: json['picUrl'],
        url: json['url']);
  }
}
class ThemeEvent extends IEvent{
  int themeModel;

  ThemeEvent(int themeModel) : super(""){
    this.themeModel = themeModel;
  }
}
class IEvent{
  String id;
  IEvent(String id): this.id = id;
}

AboutPage.dart

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'Newsdetailpage.dart';

class AboutPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("关于"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FlutterLogo(
                size: 55.0,
              ),
              Container(
                padding: EdgeInsets.all(5.0),
              ),
              Text('作者:QYK',
                  style:
                  TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold)),
              Container(
                padding: EdgeInsets.all(5.0),
              ),
              GestureDetector(
                child: Text(
                  'CSDN',
                  style: TextStyle(
                      color: Colors.blueAccent,
                      decoration:
                      TextDecoration.combine([TextDecoration.underline])),
                ),
                onTap: () {
                  Navigator.of(context).push(MaterialPageRoute(
                      builder: (ctx) => NewsDetailPage(

                      )));
                },
              )
            ],
          ),
        ));
  }
}

NewsDetailPageState.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
class NewsDetailPage extends StatefulWidget {
  final String url;
  final String title;

  const NewsDetailPage({Key key, this.url, this.title})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => NewsDetailPageState();
}

class NewsDetailPageState extends State<NewsDetailPage> {
  bool loaded = false;
  String detailDataStr;
  final flutterWebViewPlugin = FlutterWebviewPlugin();

  NewsDetailPageState({Key key});

  @override
  void initState() {
    super.initState();
    flutterWebViewPlugin.onStateChanged.listen((state) {
      print("state: ${state.type}");
      if (state.type == WebViewState.finishLoad) {
        setState(() {
          loaded = true;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {

    List<Widget> titleContent = [];
    titleContent.add(Text(
      widget.title,
      overflow: TextOverflow.ellipsis,
      style: TextStyle(
          fontSize: 13.0
      ),
      )
    );
    if (!loaded) {
      titleContent.add(CupertinoActivityIndicator());
    }
    titleContent.add(Container(width: 50.0));
    return WebviewScaffold(
      url: widget.url,
      appBar: AppBar(
        title: Expanded(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: titleContent,
          ),
        )
      ),
      withZoom: false,
      withLocalStorage: true,
      withJavascript: true,
    );
  }
}

Constant.dart

import 'package:event_bus/event_bus.dart';

class Constants {

  //全局的event_bus
  static final EventBus eventBus = EventBus();
  // ______________________________________________
  //当前的主题模式
  static int currentTheme = dayTheme;
  //日间模式
  static final int dayTheme = 1;
  //  //夜间模式
  static final int nightTheme = 2;
// ______________________________________________

}