使用天行数据网提供的新闻接口实现一个新闻推送小项目
前言
天行数据网虽然不是免费提供数据接口调用服务,但对于新注册的用户,会提供10000次的免费接口调用额度,对于具体申请的某类接口提供了100次的免费调用额度。所以要使用天行数据网提供的接口服务,首先要注册成为天行数据网的用户,然后申请某种数据接口服务。
一、注册天行网申请接口?
链接: link.点击进入天行网官网注册,并找到自己感兴趣的接口,申请即可。本文选用接口如下。
。
二、在flutter中使用http请求插件,并获取json,转化为LIST
1.引入库
代码如下(示例):
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.建立新闻类
代码如下(示例):
此处可以根据天行网说明建立类的相关属性
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请求网页
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,
);
}
}
总结
项目总体结构
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;
// ______________________________________________
}