feat: notification screen

This commit is contained in:
Rongjian Zhang 2019-01-25 20:35:20 +08:00
parent 3c83804b3d
commit 9043322201
12 changed files with 218 additions and 119 deletions

View File

@ -26,11 +26,12 @@ class HomeScreenState extends State<HomeScreen> {
@override
Widget build(context) {
// Navigator.of(context).pushNamed('/user');
final eventBloc = EventProvider.of(context);
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Home'),
middle: Text("GitFlux"),
),
child: StreamBuilder<List<Event>>(
stream: eventBloc.events,

View File

@ -17,7 +17,11 @@ class IosHomePage extends StatefulWidget {
class _IosHomePageState extends State<IosHomePage> {
@override
Widget build(context) {
return CupertinoPageScaffold(
return CupertinoTheme(
data: CupertinoThemeData(
// brightness: Brightness.dark,
// barBackgroundColor: Color.fromRGBO(0x24, 0x29, 0x2e, 1),
),
child: CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: [

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../models/notification.dart';
import '../providers/notification.dart';
import 'package:git_flux/screens/repo.dart';
class NotificationScreen extends StatefulWidget {
@override
@ -13,69 +15,115 @@ class NotificationScreenState extends State<NotificationScreen> {
// initFetch();
}
@override
void dispose() {
super.dispose();
print('dispose');
Widget _buildItem(BuildContext context, NotificationItem item) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(
title: 'test', builder: (context) => RepoScreen()));
},
child: Container(
child: Row(
children: <Widget>[
Icon(Icons.info_outline),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(item.subject.title),
Text(item.updatedAt)
],
),
),
Icon(Icons.arrow_forward_ios)
],
),
),
);
}
Widget _buildGroupItem(NotificationGroup group) {
return Container(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: CupertinoColors.extraLightBackgroundGray,
child: Text(
group.fullName,
style: TextStyle(color: CupertinoColors.black),
),
),
ListView.builder(
shrinkWrap: true,
itemCount: group.items.length,
itemBuilder: (context, index) =>
_buildItem(context, group.items[index]),
),
],
),
);
}
@override
Widget build(context) {
NotificationBloc bloc = NotificationProvider.of(context);
TextStyle _textStyle = DefaultTextStyle.of(context).style;
return SafeArea(
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: StreamBuilder<int>(
stream: bloc.active,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Text("loading...");
}
return SizedBox.expand(
child: DefaultTextStyle(
style: _textStyle,
child: CupertinoSegmentedControl(
groupValue: snapshot.data,
onValueChanged: (int index) {
bloc.activeUpdate.add(index);
},
children: {
0: Text('Unread'),
1: Text('Paticipating'),
2: Text('All')
},
),
),
);
},
),
),
child: Column(
children: <Widget>[
StreamBuilder<int>(
stream: bloc.active,
StreamBuilder<bool>(
stream: bloc.loading,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Text("loading...");
}
return Flexible(
child: snapshot.data == null || snapshot.data
? CupertinoActivityIndicator()
: StreamBuilder<List<NotificationGroup>>(
stream: bloc.items,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Text('loading...');
}
return CupertinoSegmentedControl(
groupValue: snapshot.data,
onValueChanged: (int index) {
bloc.activeUpdate.add(index);
},
children: {
0: Text('Unread'),
1: Text('Paticipating'),
2: Text('All')
},
List<NotificationGroup> groups = snapshot.data;
return ListView.builder(
itemCount: groups.length,
itemBuilder: (context, i) =>
_buildGroupItem(groups[i]),
);
},
),
);
},
),
StreamBuilder<bool>(
stream: bloc.loading,
builder: (context, snapshot) {
return Flexible(
child: snapshot.data == null || snapshot.data
? CupertinoActivityIndicator()
: StreamBuilder<List<NotificationItem>>(
stream: bloc.items,
builder: (context, snapshot) {
if (snapshot.data == null) {
return Text('loading...');
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return RichText(
text: TextSpan(
text: snapshot
.data[index].repository.fullName,
style:
TextStyle(color: CupertinoColors.black),
),
);
},
);
},
),
);
}),
],
),
);

View File

@ -4,6 +4,6 @@ import '../widgets/user.dart';
class ProfileScreen extends StatelessWidget {
@override
Widget build(context) {
return UserScreen('pd4d10');
return UserWidget('pd4d10');
}
}

View File

@ -1,9 +1,12 @@
// import 'package:uri/uri.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'ios/main.dart';
import 'providers/event.dart';
import 'providers/notification.dart';
import 'providers/search.dart';
import 'package:git_flux/providers/event.dart';
import 'package:git_flux/providers/notification.dart';
import 'package:git_flux/providers/search.dart';
import 'package:git_flux/ios/main.dart';
// import 'package:git_flux/screens/user.dart';
// import 'package:git_flux/screens/repo.dart';
class App extends StatelessWidget {
final isIos = true;
@ -22,18 +25,15 @@ class App extends StatelessWidget {
child: EventProvider(
bloc: eventBloc,
child: MaterialApp(
title: 'GitFlux',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DefaultTextStyle(
style: TextStyle(color: CupertinoColors.black),
child: IosHomePage(title: 'GitFlux'),
),
routes: {
// '/notification': (context) => IosNotificationTab(),
// '/profile': (context) => IosProfileTab(),
},
// theme: ThemeData(
// textTheme: TextTheme(
// title: TextStyle(color: Colors.red),
// ),
// ),
),
),
),

View File

@ -40,12 +40,15 @@ class Repository {
@JsonSerializable()
class NotificationItem {
NotificationItem(this.id, this.type, this.actor, this.repository);
NotificationItem(this.id, this.type, this.updatedAt, this.actor,
this.repository, this.subject);
String id;
String type;
String updatedAt;
Subject actor;
Repository repository;
Subject subject;
Map<String, dynamic> payload;
factory NotificationItem.fromJson(Map<String, dynamic> json) =>

View File

@ -42,12 +42,16 @@ NotificationItem _$NotificationItemFromJson(Map<String, dynamic> json) {
return NotificationItem(
json['id'] as String,
json['type'] as String,
json['updated_at'] as String,
json['actor'] == null
? null
: Subject.fromJson(json['actor'] as Map<String, dynamic>),
json['repository'] == null
? null
: Repository.fromJson(json['repository'] as Map<String, dynamic>))
: Repository.fromJson(json['repository'] as Map<String, dynamic>),
json['subject'] == null
? null
: Subject.fromJson(json['subject'] as Map<String, dynamic>))
..payload = json['payload'] as Map<String, dynamic>;
}
@ -55,7 +59,9 @@ Map<String, dynamic> _$NotificationItemToJson(NotificationItem instance) =>
<String, dynamic>{
'id': instance.id,
'type': instance.type,
'updated_at': instance.updatedAt,
'actor': instance.actor,
'repository': instance.repository,
'subject': instance.subject,
'payload': instance.payload
};

View File

@ -4,7 +4,14 @@ import 'package:rxdart/rxdart.dart';
import '../utils.dart';
import '../models/notification.dart';
Future<List<NotificationItem>> fetchNotifications([int index = 0]) async {
class NotificationGroup {
String fullName;
List<NotificationItem> items = [];
NotificationGroup(this.fullName);
}
Future<List<NotificationGroup>> fetchNotifications([int index = 0]) async {
String search = '';
if (index == 1) {
search = '?paticipating=true';
@ -12,18 +19,24 @@ Future<List<NotificationItem>> fetchNotifications([int index = 0]) async {
search = '?all=true';
}
List data = await getWithCredentials('/notifications$search');
// print(data.length);
return data
.map<NotificationItem>((item) => NotificationItem.fromJson(item))
.toList();
Map<String, NotificationGroup> groupMap = {};
data.forEach((item) {
String repo = item['repository']['full_name'];
if (groupMap[repo] == null) {
groupMap[repo] = NotificationGroup(repo);
}
groupMap[repo].items.add(NotificationItem.fromJson(item));
});
return groupMap.values.toList();
}
class NotificationBloc {
final _items = BehaviorSubject<List<NotificationItem>>(seedValue: []);
final _groups = BehaviorSubject<List<NotificationGroup>>(seedValue: []);
final _active = BehaviorSubject(seedValue: 0);
final _loading = BehaviorSubject(seedValue: false);
Stream<List<NotificationItem>> get items => _items.stream;
Stream<List<NotificationGroup>> get items => _groups.stream;
Stream<int> get active => _active.stream;
Stream<bool> get loading => _loading.stream;
@ -32,7 +45,7 @@ class NotificationBloc {
NotificationBloc() {
_active.stream.listen((int index) async {
_loading.add(true);
_items.add(await fetchNotifications(index));
_groups.add(await fetchNotifications(index));
_loading.add(false);
});
}

13
lib/screens/repo.dart Normal file
View File

@ -0,0 +1,13 @@
import 'package:flutter/cupertino.dart';
class RepoScreen extends StatelessWidget {
@override
Widget build(context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text("repo"),
),
child: Text("repo"),
);
}
}

9
lib/screens/user.dart Normal file
View File

@ -0,0 +1,9 @@
import 'package:flutter/cupertino.dart';
import '../widgets/user.dart';
class UserScreen extends StatelessWidget {
@override
Widget build(context) {
return UserWidget('pd4d10');
}
}

View File

@ -32,15 +32,15 @@ Future queryUser(String login) async {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
class UserScreen extends StatefulWidget {
class UserWidget extends StatefulWidget {
final String login;
UserScreen(this.login);
UserWidget(this.login);
_UserScreenState createState() => _UserScreenState();
_UserWidgetState createState() => _UserWidgetState();
}
class _UserScreenState extends State<UserScreen> {
class _UserWidgetState extends State<UserWidget> {
var user = null;
@override
@ -57,50 +57,51 @@ class _UserScreenState extends State<UserScreen> {
Widget build(BuildContext context) {
return SafeArea(
child: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () async {
var _user = await queryUser(widget.login);
setState(() {
user = _user;
});
},
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
child: ClipOval(
child: Image.network(
user['avatarUrl'],
fit: BoxFit.fill,
width: 64,
height: 64,
),
key: _refreshIndicatorKey,
onRefresh: () async {
var _user = await queryUser(widget.login);
setState(() {
user = _user;
});
},
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
child: ClipOval(
child: Image.network(
user['avatarUrl'],
fit: BoxFit.fill,
width: 64,
height: 64,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
GestureDetector(
child: Column(
children: <Widget>[
Text(user['followers']['totalCount'].toString()),
Text('Followers'),
],
),
onTap: () {
// print(1);
},
),
Column(
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
GestureDetector(
child: Column(
children: <Widget>[
Text(user['following']['totalCount'].toString()),
Text('Following')
Text(user['followers']['totalCount'].toString()),
Text('Followers'),
],
)
],
),
],
)),
),
onTap: () {
// print(1);
},
),
Column(
children: <Widget>[
Text(user['following']['totalCount'].toString()),
Text('Following')
],
)
],
),
],
),
),
);
}
}

View File

@ -21,6 +21,7 @@ dependencies:
cupertino_icons: ^0.1.2
http: ^0.11.3
rxdart: ^0.20.0
uri: ^0.11.3
dev_dependencies:
flutter_test: