This commit is contained in:
Rongjian Zhang 2018-07-05 22:08:19 +08:00
parent d6aeb873f1
commit dd456132f4
15 changed files with 576 additions and 367 deletions

0
lib/android/drawer.dart Normal file
View File

111
lib/android/home.dart Normal file
View File

@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../common/event.dart';
import '../utils.dart';
import 'dart:async';
class HomePage extends StatefulWidget {
// HomePage({Key key, this.title, this.message}) : super(key: key);
HomePage(this.message);
// final String title;
final Function message;
@override
createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int page = 1;
List<Event> events = [];
bool isLoading = false;
loadFirst() async {
var data = await fetchEvents();
setState(() {
events.clear();
events.addAll(data);
// print(events);
page = 1;
// isLoading = false;
});
return data;
}
loadMore() async {
// if (isLoading) return;
// print('more');
setState(() {
isLoading = true;
});
var data = await fetchEvents(page + 1);
setState(() {
events.addAll(data);
// print(events.length);
page++;
isLoading = false;
});
}
final _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
@override
build(BuildContext context) {
return FutureBuilder(
future: loadFirst(),
builder: (context, snapshot) {
Widget widget;
if (snapshot.hasData) {
widget = RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () async {
await loadFirst();
return null;
},
child: ListView.builder(
// reverse: true,
itemBuilder: (context, index) {
try {
if (index >= events.length) {
// print(events.length);
if (!isLoading) {
print('index: $index');
print('length: ${events.length}');
loadMore();
}
return Text('Loading...');
// print(index);
// Load next page
// return CupertinoActivityIndicator();
// if (isLoading) {
// return Text('Loading...');
// } else {
// // print(isLoading);
// // setState(() {
// // isLoading = true;
// // });
// return CupertinoRefreshControl();
// }
// return Text('Loading...');
}
return EventItem(events[index]);
} catch (err) {
return Text(err.toString());
// return null;
}
},
),
);
} else if (snapshot.hasError) {
widget = Text("${snapshot.error}");
} else {
widget = CupertinoActivityIndicator();
// widget = null;
}
return widget;
},
);
}
}

View File

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'user.dart';
import 'home.dart';
class AndroidHomePage extends StatefulWidget {
AndroidHomePage({Key key, this.title}) : super(key: key);
@ -6,7 +8,7 @@ class AndroidHomePage extends StatefulWidget {
final String title;
@override
_AndroidHomePageState createState() => new _AndroidHomePageState();
_AndroidHomePageState createState() => _AndroidHomePageState();
}
class _AndroidHomePageState extends State<AndroidHomePage> {
@ -18,17 +20,34 @@ class _AndroidHomePageState extends State<AndroidHomePage> {
});
}
final _scaffoldKey = GlobalKey<ScaffoldState>();
message() {
_scaffoldKey.currentState?.showSnackBar(
SnackBar(
content: Text('Refresh complete'),
action: SnackBarAction(
label: 'RETRY',
onPressed: () {
// _refreshIndicatorKey.currentState.show();
},
),
),
);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
drawer: new Drawer(
child: new Column(
return Scaffold(
key: _scaffoldKey,
drawer: Drawer(
child: Column(
children: [
new UserAccountsDrawerHeader(
accountName: const Text('xxx'),
accountEmail: const Text('xxx@gmail.com'),
currentAccountPicture: const CircleAvatar(
backgroundImage: const NetworkImage(
UserAccountsDrawerHeader(
accountName: Text('123'),
accountEmail: Text('xxx@gmail.com'),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(
'https://avatars0.githubusercontent.com/u/9524411',
),
),
@ -36,24 +55,37 @@ class _AndroidHomePageState extends State<AndroidHomePage> {
// onDetailsPressed: () {
// },
),
new MediaQuery.removePadding(
MediaQuery.removePadding(
context: context,
// DrawerHeader consumes top MediaQuery padding.
removeTop: true,
child: new Expanded(
child: new ListView(
padding: const EdgeInsets.only(top: 8.0),
children: <Widget>[
new Stack(
child: Expanded(
child: ListView(
padding: EdgeInsets.only(top: 8.0),
children: [
Stack(
children: [
new Column(
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: ['A', 'B'].map((String id) {
return new ListTile(
leading: new CircleAvatar(child: new Text(id)),
title: new Text('Drawer item $id'),
onTap: () {},
children: ['Home', 'Settings'].map((id) {
return ListTile(
// leading: CircleAvatar(child: Text(id)),
title: Text(id),
onTap: () {
var route = PageRouteBuilder(
settings: RouteSettings(
name: 'test',
isInitialRoute: false,
),
pageBuilder:
(context, animation, secondaryAnimation) {
return UserPage('a', 'avatar');
},
);
Navigator.of(context).push(route);
},
);
}).toList(),
)
@ -66,27 +98,14 @@ class _AndroidHomePageState extends State<AndroidHomePage> {
],
),
),
appBar: new AppBar(
title: new Text(widget.title),
appBar: AppBar(
title: Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
body: HomePage(message),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
child: Icon(Icons.add),
),
);
}

58
lib/android/user.dart Normal file
View File

@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
// import '../utils.dart';
enum AppBarBehavior { normal, pinned, floating, snapping }
class UserPage extends StatelessWidget {
final String login;
final String avatar;
UserPage(this.login, this.avatar);
final double _appBarHeight = 256.0;
AppBarBehavior _appBarBehavior = AppBarBehavior.pinned;
@override
build(context) {
return Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
expandedHeight: _appBarHeight,
pinned: _appBarBehavior == AppBarBehavior.pinned,
floating: _appBarBehavior == AppBarBehavior.floating ||
_appBarBehavior == AppBarBehavior.snapping,
snap: _appBarBehavior == AppBarBehavior.snapping,
actions: [
IconButton(
icon: Icon(Icons.edit),
tooltip: 'Edit',
onPressed: () {
// _scaffoldKey.currentState.showSnackBar( SnackBar(
// content: Text(
// 'This is actually just a demo. Editing isn\'t supported.')));
},
),
],
flexibleSpace: FlexibleSpaceBar(
title: Text(login),
background: Stack(
fit: StackFit.expand,
children: [
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment(0.0, -1.0),
end: Alignment(0.0, -0.4),
colors: <Color>[
Color(0x60000000),
Color(0x00000000)
],
),
),
),
],
),
),
),
]));
}
}

205
lib/common/event.dart Normal file
View File

@ -0,0 +1,205 @@
import '../utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
// import '../issue.dart';
// import '../user.dart';
// class Strong extends StatelessWidget {
// final String text;
// @override
// build(context) {
// return TextSpan(
// text: text,
// style: TextStyle(
// fontWeight: FontWeight.bold,
// color: Color(0xff24292e),
// ),
// // recognizer: recognizer,
// );
// }
// }
class _Avatar extends StatelessWidget {
final String url;
_Avatar(this.url);
@override
build(context) {
return CircleAvatar(
backgroundImage: NetworkImage(url),
radius: 24.0,
);
}
}
class EventItem extends StatelessWidget {
final Event event;
EventItem(this.event);
getEventItemByType() {
switch (event.type) {
case 'IssuesEvent':
return IssuesEvent(event);
case 'PushEvent':
return PushEvent(event);
case 'PullRequestEvent':
return PullRequestEvent(event);
case 'WatchEvent':
return WatchEvent(event);
default:
return Text('Not implement yet');
}
}
@override
build(context) {
// Padding(padding: EdgeInsets.only(top: 16.0)),
return Row(
children: [
_Avatar(event.avatar),
Expanded(child: getEventItemByType()),
],
);
}
}
TextSpan _strong(String text, [GestureRecognizer recognizer]) {
return TextSpan(
text: text,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xff24292e),
),
recognizer: recognizer,
);
}
TextSpan _user(Event event, context) {
return _strong(
event.actor,
// TapGestureRecognizer()
// ..onTap = () {
// Navigator.of(context).push(
// CupertinoPageRoute(
// builder: (context) {
// return IosUserPage(event.actor, event.avatar);
// },
// ),
// );
// },
);
}
class PushEvent extends StatelessWidget {
final Event event;
PushEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' pushed to '),
TextSpan(
text: event.payload['ref'],
style: TextStyle(color: CupertinoColors.activeBlue),
),
TextSpan(text: ' in '),
_strong(event.repo),
TextSpan(text: '')
],
),
);
}
}
class IssuesEvent extends StatelessWidget {
final Event event;
IssuesEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} issue '),
_strong(event.repo),
TextSpan(
text: '#' + event.payload['issue']['number'].toString(),
),
TextSpan(
text: event.payload['issue']['title'],
)
],
),
);
}
}
class PullRequestEvent extends StatelessWidget {
final Event event;
PullRequestEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} pull request '),
_strong(event.repo),
TextSpan(text: '#' + event.payload['number'].toString()),
TextSpan(text: event.payload['pull_request']['title'])
],
),
);
}
}
class IssueCommentEvent extends StatelessWidget {
final Event event;
IssueCommentEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' commented on issue '),
_strong(event.repo),
TextSpan(text: '#' + event.payload['issue']['number'].toString()),
TextSpan(text: event.payload['comment']['body'])
],
),
);
}
}
class WatchEvent extends StatelessWidget {
final Event event;
WatchEvent(this.event);
@override
build(context) {
return RichText(
text: TextSpan(
style: TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
TextSpan(text: ' ${event.payload['action']} '),
_strong(event.repo),
],
),
);
}
}

78
lib/ios/home.dart Normal file
View File

@ -0,0 +1,78 @@
// import 'dart:async';
// import 'dart:convert';
import '../utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import '../common/event.dart';
import 'user.dart';
class IosHomeTab extends StatefulWidget {
@override
createState() {
return IosHomeTabState();
}
}
class IosHomeTabState extends State<IosHomeTab> {
int page = 1;
List<Event> events = [];
loadFirst() async {
events = await fetchEvents();
print(events);
page = 1;
return events;
}
loadMore() async {
events.addAll(await fetchEvents(page + 1));
page++;
return events;
}
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
@override
Widget build(context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Home'),
),
child: FutureBuilder(
future: loadFirst(),
builder: (context, snapshot) {
Widget widget;
if (snapshot.hasData) {
// List<Event> events = snapshot.data;
// ScrollController(debugLabel: 'aaa', )
// widget = CupertinoRefreshControl(
// // key: _refreshIndicatorKey,
// onRefresh: () {
// print(1);
// loadFirst();
// },
// );
widget = ListView.builder(itemBuilder: (context, index) {
print(index);
try {
return EventItem(events[index]);
} catch (err) {
return Text(err.toString());
// return null;
}
});
} else if (snapshot.hasError) {
widget = Text("${snapshot.error}");
} else {
widget = CupertinoActivityIndicator();
}
return widget;
},
),
);
}
}

View File

@ -1,144 +0,0 @@
import '../../utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import '../issue.dart';
import '../user.dart';
TextSpan _strong(String text, [GestureRecognizer recognizer]) {
return new TextSpan(
text: text,
style: new TextStyle(
fontWeight: FontWeight.bold,
color: new Color(0xff24292e),
),
recognizer: recognizer,
);
}
TextSpan _user(Event event, context) {
return _strong(
event.actor,
new TapGestureRecognizer()
..onTap = () {
Navigator.of(context).push(
new CupertinoPageRoute(
builder: (context) {
return new IosUserPage(event.actor, event.avatar);
},
),
);
},
);
}
class PushEvent extends StatelessWidget {
final Event event;
PushEvent(this.event);
@override
build(context) {
return new RichText(
text: new TextSpan(
style: new TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
new TextSpan(text: ' pushed to '),
new TextSpan(
text: event.payload['ref'],
style: new TextStyle(color: CupertinoColors.activeBlue),
),
new TextSpan(text: ' in '),
_strong(event.repo),
new TextSpan(text: '')
],
),
);
}
}
class IssuesEvent extends StatelessWidget {
final Event event;
IssuesEvent(this.event);
@override
build(context) {
return new RichText(
text: new TextSpan(
style: new TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
new TextSpan(text: ' ${event.payload['action']} issue '),
_strong(event.repo),
new TextSpan(
text: '#' + event.payload['issue']['number'].toString(),
),
new TextSpan(
text: event.payload['issue']['title'],
)
],
),
);
}
}
class PullRequestEvent extends StatelessWidget {
final Event event;
PullRequestEvent(this.event);
@override
build(context) {
return new RichText(
text: new TextSpan(
style: new TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
new TextSpan(text: ' ${event.payload['action']} pull request '),
_strong(event.repo),
new TextSpan(text: '#' + event.payload['number'].toString()),
new TextSpan(text: event.payload['pull_request']['title'])
],
),
);
}
}
class IssueCommentEvent extends StatelessWidget {
final Event event;
IssueCommentEvent(this.event);
@override
build(context) {
return new RichText(
text: new TextSpan(
style: new TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
new TextSpan(text: ' commented on issue '),
_strong(event.repo),
new TextSpan(text: '#' + event.payload['issue']['number'].toString()),
new TextSpan(text: event.payload['comment']['body'])
],
),
);
}
}
class WatchEvent extends StatelessWidget {
final Event event;
WatchEvent(this.event);
@override
build(context) {
return new RichText(
text: new TextSpan(
style: new TextStyle(color: CupertinoColors.black),
children: [
_user(event, context),
new TextSpan(text: ' ${event.payload['action']} '),
_strong(event.repo),
],
),
);
}
}

View File

@ -1,121 +0,0 @@
// import 'dart:async';
// import 'dart:convert';
import '../../utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'event.dart';
import '../user.dart';
class _Avatar extends StatelessWidget {
final String avatar;
_Avatar(this.avatar);
@override
build(context) {
return new CircleAvatar(
backgroundImage: NetworkImage(avatar),
radius: 24.0,
);
}
}
class _EventItem extends StatelessWidget {
final Event event;
final Widget child;
_EventItem(this.event, this.child);
@override
build(context) {
// const Padding(padding: const EdgeInsets.only(top: 16.0)),
return new Row(
children: [
new _Avatar(event.avatar),
new Expanded(child: child),
],
);
}
}
class IosHomeTab extends StatefulWidget {
@override
createState() {
return new IosHomeTabState();
}
}
class IosHomeTabState extends State<IosHomeTab> {
int page = 1;
List<Event> events = [];
loadFirst() async {
events = await fetchEvents();
page = 1;
return events;
}
loadMore() async {
events.addAll(await fetchEvents(page + 1));
page++;
return events;
}
@override
Widget build(context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
middle: new Text('Home'),
),
child: new FutureBuilder(
future: loadFirst(),
builder: (context, snapshot) {
Widget widget;
if (snapshot.hasData) {
// List<Event> events = snapshot.data;
widget = new CustomScrollView(
slivers: [
new CupertinoRefreshControl(
onRefresh: () {
return loadFirst();
},
),
new SliverSafeArea(
// top: true,
sliver: new SliverList(
delegate: new SliverChildBuilderDelegate(
(context, index) {
var event = events[index];
var child = (() {
switch (event.type) {
case 'IssuesEvent':
return new IssuesEvent(event);
case 'PushEvent':
return new PushEvent(event);
case 'PullRequestEvent':
return new PullRequestEvent(event);
case 'WatchEvent':
return new WatchEvent(event);
default:
return new Text('Not implement yet');
}
})();
return new _EventItem(event, child);
},
childCount: 30,
),
),
),
],
);
} else if (snapshot.hasError) {
widget = new Text("${snapshot.error}");
} else {
widget = new CupertinoActivityIndicator();
}
return widget;
},
),
);
}
}

View File

@ -6,6 +6,6 @@ import 'package:flutter/cupertino.dart';
class IosIssue extends StatelessWidget {
@override
Widget build(context) {
return new Text('Issue');
return Text('Issue');
}
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'home/index.dart';
import 'home.dart';
import 'notification.dart';
import 'profile.dart';
import 'issue.dart';
@ -11,7 +11,7 @@ class IosHomePage extends StatefulWidget {
final String title;
@override
_IosHomePageState createState() => new _IosHomePageState();
_IosHomePageState createState() => _IosHomePageState();
}
class _IosHomePageState extends State<IosHomePage> {
@ -25,42 +25,42 @@ class _IosHomePageState extends State<IosHomePage> {
@override
Widget build(context) {
return new CupertinoPageScaffold(
child: new CupertinoTabScaffold(
tabBar: new CupertinoTabBar(
items: const <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: const Icon(CupertinoIcons.home),
title: const Text('Home'),
return CupertinoPageScaffold(
child: CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
title: Text('Home'),
),
const BottomNavigationBarItem(
icon: const Icon(CupertinoIcons.conversation_bubble),
title: const Text('Notification'),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.conversation_bubble),
title: Text('Notification'),
),
const BottomNavigationBarItem(
icon: const Icon(CupertinoIcons.profile_circled),
title: const Text('Profile'),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
title: Text('Profile'),
),
],
),
tabBuilder: (context, int index) {
return new DefaultTextStyle(
style: const TextStyle(
fontFamily: '.SF UI Text',
fontSize: 17.0,
tabBuilder: (context, index) {
return DefaultTextStyle(
style: TextStyle(
// fontFamily: '.SF UI Text',
// fontSize: 17.0,
color: CupertinoColors.black,
),
child: new CupertinoTabView(
child: CupertinoTabView(
builder: (BuildContext context) {
switch (index) {
case 0:
return new IosHomeTab();
return IosHomeTab();
break;
case 1:
return new IosNotificationTab();
return IosNotificationTab();
break;
case 2:
return new IosProfileTab();
return IosProfileTab();
break;
default:
}

View File

@ -6,6 +6,6 @@ import 'package:flutter/cupertino.dart';
class IosNotificationTab extends StatelessWidget {
@override
Widget build(context) {
return new Text('Notification');
return Text('Notification');
}
}

View File

@ -6,6 +6,6 @@ import 'package:flutter/cupertino.dart';
class IosProfileTab extends StatelessWidget {
@override
Widget build(context) {
return new Text('Profile');
return Text('Profile');
}
}

View File

@ -9,31 +9,32 @@ class IosUserPage extends StatelessWidget {
@override
build(context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
leading: new CupertinoButton(
child: const Text('Cancel'),
padding: EdgeInsets.zero,
onPressed: () {
Navigator.of(context).pop(false);
},
),
middle: new Text(login),
),
child: new FutureBuilder(
future: fetchUser(login),
builder: (context, snapshot) {
Widget widget;
if (snapshot.hasData) {
User user = snapshot.data;
return new Text('');
} else if (snapshot.hasError) {
widget = new Text("${snapshot.error}");
} else {
widget = new CupertinoActivityIndicator();
}
return widget;
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: CupertinoButton(
child: Text('Cancel'),
padding: EdgeInsets.zero,
onPressed: () {
Navigator.of(context).pop(false);
},
));
),
middle: Text(login),
),
child: FutureBuilder(
future: fetchUser(login),
builder: (context, snapshot) {
Widget widget;
if (snapshot.hasData) {
User user = snapshot.data;
return Text('');
} else if (snapshot.hasError) {
widget = Text("${snapshot.error}");
} else {
widget = CupertinoActivityIndicator();
}
return widget;
},
),
);
}
}

View File

@ -3,22 +3,24 @@ import 'android/main.dart';
import 'ios/main.dart';
class App extends StatelessWidget {
final isIos = true;
final isIos = false;
@override
build(context) {
return new MaterialApp(
return MaterialApp(
title: 'GitFlux',
theme: new ThemeData(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: isIos ? new IosHomePage(title: 'GitFlux') : new AndroidHomePage(title: 'GitFlux'),
home: isIos
? IosHomePage(title: 'GitFlux')
: AndroidHomePage(title: 'GitFlux'),
routes: {
// '/notification': (context) => new IosNotificationTab(),
// '/profile': (context) => new IosProfileTab(),
// '/notification': (context) => IosNotificationTab(),
// '/profile': (context) => IosProfileTab(),
},
);
}
}
void main() => runApp(new App());
void main() => runApp(App());

View File

@ -14,7 +14,7 @@ class Event {
String repo;
Map<String, dynamic> payload;
Event(data) {
Event.fromJson(data) {
id = data['id'];
type = data['type'];
actor = data['actor']['login'];
@ -32,7 +32,7 @@ class User {
int followers;
int following;
User(data) {
User.fromJson(data) {
login = data['login'];
avatar = data['avatar_url'];
name = data['name'];
@ -50,7 +50,7 @@ Future<List<Event>> fetchEvents([int page = 1]) async {
List<dynamic> data = json.decode(res.body);
return data.map((item) {
return new Event(item);
return Event.fromJson(item);
}).toList();
}
@ -61,5 +61,5 @@ Future<User> fetchUser(String login) async {
);
Map<String, dynamic> data = json.decode(res.body);
return new User(data);
return User.fromJson(data);
}