feat: add trending screen

This commit is contained in:
Rongjian Zhang 2019-03-02 18:17:46 +08:00
parent e6d6835661
commit c06ef90618
4 changed files with 120 additions and 13 deletions

View File

@ -9,6 +9,7 @@ import 'screens/me.dart';
import 'screens/login.dart';
import 'screens/issue.dart';
import 'screens/repos.dart';
import 'screens/trending.dart';
class Home extends StatefulWidget {
@override
@ -49,6 +50,10 @@ class _HomeState extends State<Home> {
icon: _buildNotificationIcon(context),
title: Text('Notification'),
),
BottomNavigationBarItem(
icon: Icon(Icons.trending_up),
title: Text('Trending'),
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
title: Text('Search'),
@ -63,14 +68,17 @@ class _HomeState extends State<Home> {
_buildScreen(int index) {
// return IssueScreen(number: 29, owner: 'reactjs', name: 'rfcs');
// return ReposScreen('pd4d10');
// return TrendingScreen();
switch (index) {
case 0:
return NewsScreen();
case 1:
return NotificationScreen();
case 2:
return SearchScreen();
return TrendingScreen();
case 3:
return SearchScreen();
case 4:
return MeScreen();
}
}

97
lib/screens/trending.dart Normal file
View File

@ -0,0 +1,97 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:html/parser.dart' show parse;
import '../scaffolds/refresh.dart';
import '../widgets/repo_item.dart';
// class TrendingRepo {
// String owner;
// String name;
// String description;
// String languageName;
// String languageColor;
// int starCount;
// int forkCount;
// int stars;
// }
class TrendingScreen extends StatefulWidget {
@override
_TrendingScreenState createState() => _TrendingScreenState();
}
class _TrendingScreenState extends State<TrendingScreen> {
Future<List<dynamic>> _fetchTrendingRepos() async {
var res = await http.get('https://github.com/trending');
// print(res.body);
var document = parse(res.body);
var items = document.querySelectorAll('.repo-list>li');
return items.map((item) {
Map<String, String> lang;
var colorNode = item.querySelector('.repo-language-color');
if (colorNode != null) {
lang = {
'name': colorNode.nextElementSibling.innerHtml.trim(),
'color': RegExp(r'(#\w{6})')
.firstMatch(colorNode.attributes['style'])
.group(0),
};
}
var payload = {
'owner': {
'login': item
.querySelector('h3>a>span')
?.innerHtml
?.replaceFirst('/', '')
?.trim()
},
'name': item
.querySelector('h3>a')
?.innerHtml
?.replaceFirst(RegExp(r'^[\s\S]*span>'), '')
?.trim(),
'description': item.children[2].querySelector('p')?.innerHtml?.trim(),
'stargazers': {
'totalCount': item.children[3]
.querySelectorAll('a')[0]
?.innerHtml
?.replaceFirst(RegExp(r'^[\s\S]*svg>'), '')
?.trim()
},
'forks': {
'totalCount': item.children[3]
.querySelectorAll('a')[1]
?.innerHtml
?.replaceFirst(RegExp(r'^[\s\S]*svg>'), '')
?.trim(),
},
'primaryLanguage': lang,
'isPrivate': false,
'isFork': false // TODO:
};
// print(payload);
return payload;
}).toList();
}
@override
Widget build(BuildContext context) {
return RefreshScaffold(
title: Text('Trending'),
onRefresh: _fetchTrendingRepos,
bodyBuilder: (payload) {
return Column(
children: payload.map<Widget>((repo) {
return Container(
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black12))),
child: RepoItem(repo),
);
}).toList(),
);
},
);
}
}

View File

@ -5,15 +5,15 @@ import '../screens/repo.dart';
import 'link.dart';
class RepoItem extends StatelessWidget {
final Map<String, dynamic> item;
final Map<String, dynamic> payload;
RepoItem(this.item);
RepoItem(this.payload);
IconData _buildIconData() {
if (item['isPrivate']) {
if (payload['isPrivate']) {
return Octicons.lock;
}
if (item['isFork']) {
if (payload['isFork']) {
return Octicons.repo_forked;
}
return Octicons.repo;
@ -22,7 +22,8 @@ class RepoItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Link(
screenBuilder: (_) => RepoScreen(item['owner']['login'], item['name']),
screenBuilder: (_) =>
RepoScreen(payload['owner']['login'], payload['name']),
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
@ -33,24 +34,24 @@ class RepoItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item['owner']['login'] + '/' + item['name'],
payload['owner']['login'] + '/' + payload['name'],
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15),
),
Padding(padding: EdgeInsets.only(top: 6)),
Text(item['description'] ?? 'No description provided yet'),
Text(payload['description'] ?? 'No description provided yet'),
Padding(padding: EdgeInsets.only(top: 6)),
DefaultTextStyle(
style: TextStyle(color: Colors.black54, fontSize: 13),
child: Row(
children: <Widget>[
Icon(Octicons.star, size: 14, color: Colors.black54),
Text(item['stargazers']['totalCount'].toString()),
Text(payload['stargazers']['totalCount'].toString()),
Padding(padding: EdgeInsets.only(left: 16)),
Icon(Octicons.repo_forked,
size: 14, color: Colors.black54),
Text(item['forks']['totalCount'].toString()),
Text(payload['forks']['totalCount'].toString()),
Padding(padding: EdgeInsets.only(left: 16)),
item['primaryLanguage'] == null
payload['primaryLanguage'] == null
? Container()
: Row(children: <Widget>[
Container(
@ -58,12 +59,12 @@ class RepoItem extends StatelessWidget {
height: 10,
decoration: new BoxDecoration(
color: convertColor(
item['primaryLanguage']['color']),
payload['primaryLanguage']['color']),
shape: BoxShape.circle,
),
),
Padding(padding: EdgeInsets.only(left: 4)),
Text(item['primaryLanguage']['name']),
Text(payload['primaryLanguage']['name']),
]),
],
),

View File

@ -27,6 +27,7 @@ dependencies:
nanoid: ^0.0.6
share: ^0.6.0
flutter_vector_icons: ^0.0.2
html: ^0.13.3
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.