From b07aee5aad6cd5117c41d82d033e8bca9dc4a896 Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Wed, 30 Jan 2019 14:46:18 +0800 Subject: [PATCH] feat: add android notification screen --- lib/android/android.dart | 3 + lib/android/news.dart | 10 ++- lib/android/notifications.dart | 97 +++++++++++++++++++++++++- lib/ios/news.dart | 2 +- lib/ios/notification.dart | 107 +++-------------------------- lib/main.dart | 10 +-- lib/providers/event.dart | 49 ------------- lib/providers/providers.dart | 1 - lib/providers/settings.dart | 39 +++++++++++ lib/widgets/news_provider.dart | 5 +- lib/widgets/notification_item.dart | 94 +++++++++++++++++++++++++ pubspec.yaml | 5 -- 12 files changed, 259 insertions(+), 163 deletions(-) delete mode 100644 lib/providers/event.dart create mode 100644 lib/providers/settings.dart create mode 100644 lib/widgets/notification_item.dart diff --git a/lib/android/android.dart b/lib/android/android.dart index 6bf8f29..ebfd749 100644 --- a/lib/android/android.dart +++ b/lib/android/android.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:git_flux/utils/utils.dart'; import 'news.dart'; +import 'notifications.dart'; class AndroidHome extends StatefulWidget { @override @@ -15,6 +16,8 @@ class _AndroidHomeState extends State { switch (active) { case 0: return NewsScreen(); + case 1: + return NotificationScreen(); default: return Text('d'); } diff --git a/lib/android/news.dart b/lib/android/news.dart index 0d0a100..1527686 100644 --- a/lib/android/news.dart +++ b/lib/android/news.dart @@ -19,7 +19,15 @@ class NewsScreenState extends State { return Scaffold( appBar: AppBar(title: Text('News')), body: RefreshIndicator( - onRefresh: refresh, + onRefresh: () async { + await refresh(); + // Scaffold.of(context).showSnackBar(SnackBar( + // content: Container( + // child: Text('data'), + // padding: EdgeInsets.only(bottom: 10), + // ) + // )); + }, child: ListView.builder( controller: controller, itemCount: events.length + 1, diff --git a/lib/android/notifications.dart b/lib/android/notifications.dart index a47f241..a35dd6d 100644 --- a/lib/android/notifications.dart +++ b/lib/android/notifications.dart @@ -1 +1,96 @@ -import 'package:flutter/material.dart'; +import 'dart:core'; +import 'package:flutter/material.dart' hide Notification; +import 'package:flutter/cupertino.dart' hide Notification; +import 'package:git_flux/providers/notification.dart'; +import 'package:git_flux/widgets/notification_item.dart'; +import 'package:git_flux/utils/utils.dart'; + +class NotificationGroup { + String fullName; + List items = []; + + NotificationGroup(this.fullName); +} + +class NotificationScreen extends StatefulWidget { + @override + NotificationScreenState createState() => NotificationScreenState(); +} + +class NotificationScreenState extends State { + int active = 0; + bool loading = false; + List groups = []; + + @override + void initState() { + super.initState(); + Future.delayed(Duration(seconds: 0)).then((_) { + _onSwitchTab(context, 0); + }); + } + + Widget _buildGroupItem(BuildContext context, int index) { + var group = groups[index]; + + return Container( + // padding: EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + padding: EdgeInsets.only(top: 10, left: 4, bottom: 4), + color: CupertinoColors.extraLightBackgroundGray, + child: Text( + group.fullName, + style: TextStyle(color: CupertinoColors.black), + ), + ), + Column( + children: group.items + .map((item) => NotificationItem(item: item)) + .toList()) + ], + ), + ); + } + + void _onSwitchTab(BuildContext context, int index) async { + setState(() { + active = index; + loading = true; + }); + + var ns = await ghClient.activity + .listNotifications(all: index == 2, participating: index == 1) + .toList(); + + NotificationProvider.of(context).countUpdate.add(ns.length); + + Map groupMap = {}; + ns.forEach((item) { + String repo = item.repository.fullName; + if (groupMap[repo] == null) { + groupMap[repo] = NotificationGroup(repo); + } + + groupMap[repo].items.add(item); + }); + + setState(() { + groups = groupMap.values.toList(); + loading = false; + }); + } + + @override + Widget build(context) { + return Scaffold( + appBar: AppBar(title: Text('Notification')), + body: ListView.builder( + itemCount: groups.length, + itemBuilder: _buildGroupItem, + ), + ); + } +} diff --git a/lib/ios/news.dart b/lib/ios/news.dart index a72fd5a..04b808d 100644 --- a/lib/ios/news.dart +++ b/lib/ios/news.dart @@ -25,7 +25,7 @@ class NewsScreenState extends State { slivers: [ CupertinoSliverNavigationBar( largeTitle: const Text('News'), - trailing: Icon(Octicons.settings), + // trailing: Icon(Octicons.settings), ), CupertinoSliverRefreshControl( onRefresh: refresh, diff --git a/lib/ios/notification.dart b/lib/ios/notification.dart index 947312a..6389be5 100644 --- a/lib/ios/notification.dart +++ b/lib/ios/notification.dart @@ -1,8 +1,7 @@ -import 'dart:core'; import 'package:flutter/material.dart' hide Notification; import 'package:flutter/cupertino.dart' hide Notification; import 'package:git_flux/providers/notification.dart'; -import 'package:git_flux/screens/screens.dart'; +import 'package:git_flux/widgets/notification_item.dart'; import 'package:git_flux/utils/utils.dart'; class NotificationGroup { @@ -30,86 +29,6 @@ class NotificationScreenState extends State { }); } - Widget _buildRoute(Notification item) { - String type = item.subject.type; - switch (type) { - case 'Issue': - case 'PullRequest': - // return IssueScreen(item.repository.); - default: - throw new Exception('Unhandled notification type: $type'); - } - } - - IconData _buildIconData(String type) { - switch (type) { - case 'Issue': - return Octicons.issue_opened; - // color: Color.fromRGBO(0x28, 0xa7, 0x45, 1), - case 'PullRequest': - return Octicons.git_pull_request; - // color: Color.fromRGBO(0x6f, 0x42, 0xc1, 1), - default: - throw new Exception('Unhandled icon type: $type'); - } - } - - Widget _buildItem(BuildContext context, Notification item) { - return Material( - child: InkWell( - splashColor: Colors.transparent, - onTap: () { - Navigator.of(context).push( - CupertinoPageRoute(builder: (context) => _buildRoute(item)), - ); - }, - child: Container( - child: Row( - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Icon(_buildIconData(item.subject.type), - color: CupertinoColors.inactiveGray), - ), - Expanded( - child: Container( - decoration: BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.grey))), - child: Row( - children: [ - Expanded( - child: Container( - padding: EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(item.subject.title, - style: TextStyle(height: 1)), - Padding(padding: EdgeInsets.only(top: 4)), - Text(TimeAgo.format(item.updatedAt), - style: TextStyle(fontSize: 12)) - ], - ), - ), - ), - Container( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Icon( - CupertinoIcons.right_chevron, - color: CupertinoColors.inactiveGray, - ), - ), - ], - ), - ), - ), - ], - ), - ), - ), - ); - } - Widget _buildGroupItem(BuildContext context, int index) { var group = groups[index]; @@ -127,8 +46,9 @@ class NotificationScreenState extends State { ), ), Column( - children: - group.items.map((item) => _buildItem(context, item)).toList()) + children: group.items + .map((item) => NotificationItem(item: item)) + .toList()) ], ), ); @@ -184,21 +104,10 @@ class NotificationScreenState extends State { ), ), ), - child: Column( - children: [ - // CupertinoSliverRefreshControl( - // onRefresh: () async { - // return Future.delayed(Duration(seconds: 3)); - // }, - // ), - Container( - child: ListView.builder( - shrinkWrap: true, - itemCount: groups.length, - itemBuilder: _buildGroupItem, - ), - ), - ], + child: ListView.builder( + shrinkWrap: true, + itemCount: groups.length, + itemBuilder: _buildGroupItem, ), ); } diff --git a/lib/main.dart b/lib/main.dart index 0463e90..51e4e43 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,18 +1,18 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -// import 'package:device_info/device_info.dart'; import 'package:git_flux/providers/providers.dart'; +import 'package:git_flux/providers/settings.dart'; import 'package:git_flux/ios/ios.dart'; import 'package:git_flux/android/android.dart'; class App extends StatelessWidget { final isIos = Platform.isIOS; - final EventBloc eventBloc; + final SettingsBloc settingsBloc; final NotificationBloc notificationBloc; final SearchBloc searchBloc; - App(this.eventBloc, this.notificationBloc, this.searchBloc); + App(this.settingsBloc, this.notificationBloc, this.searchBloc); _buildScreen() { // return IssueScreen(11609, 'flutter', 'flutter'); @@ -33,7 +33,7 @@ class App extends StatelessWidget { child: NotificationProvider( bloc: notificationBloc, child: EventProvider( - bloc: eventBloc, + bloc: settingsBloc, child: MaterialApp( home: DefaultTextStyle( style: TextStyle(color: Color(0xff24292e)), @@ -52,7 +52,7 @@ class App extends StatelessWidget { } void main() async { - EventBloc eventBloc = EventBloc(); + SettingsBloc eventBloc = SettingsBloc(); NotificationBloc notificationBloc = NotificationBloc(); SearchBloc searchBloc = SearchBloc(); diff --git a/lib/providers/event.dart b/lib/providers/event.dart deleted file mode 100644 index 5516ecb..0000000 --- a/lib/providers/event.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:async'; -import 'package:flutter/widgets.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:git_flux/utils/utils.dart'; - -class EventBloc { - final _items = BehaviorSubject>(seedValue: []); - final _page = BehaviorSubject(seedValue: 0); - final _update = StreamController(); - - // Stream get eventPage => _page.stream; - Stream> get events => _items.stream; - Sink get update => _update.sink; - - EventBloc() { - // _update.stream.listen((bool isRefresh) async { - // if (isRefresh) { - // _items.add(await fetchEvents(1)); - // _page.add(1); - // } else { - // _items.add(await fetchEvents(1)); - // _page.add(_page.value + 1); - // } - // }); - } - - void dispose() { - _items.close(); - _page.close(); - } -} - -class EventProvider extends InheritedWidget { - final EventBloc bloc; - - EventProvider({ - Key key, - Widget child, - @required EventBloc bloc, - }) : bloc = bloc, - super(key: key, child: child); - - @override - bool updateShouldNotify(InheritedWidget oldWidget) => true; - - static EventBloc of(BuildContext context) => - (context.inheritFromWidgetOfExactType(EventProvider) as EventProvider) - .bloc; -} diff --git a/lib/providers/providers.dart b/lib/providers/providers.dart index 11caaa0..ca1612b 100644 --- a/lib/providers/providers.dart +++ b/lib/providers/providers.dart @@ -1,4 +1,3 @@ -export 'event.dart'; export 'notification.dart'; export 'search.dart'; export 'user.dart'; diff --git a/lib/providers/settings.dart b/lib/providers/settings.dart new file mode 100644 index 0000000..0356b07 --- /dev/null +++ b/lib/providers/settings.dart @@ -0,0 +1,39 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart'; +import 'package:rxdart/rxdart.dart'; +import 'package:git_flux/utils/utils.dart'; + +class LayoutMap { + static const material = 0; + static const cupertino = 1; +} + +class SettingsBloc { + final _layout = BehaviorSubject(seedValue: LayoutMap.material); + // final _update = StreamController(); + + Stream get layout { + // _layout.value + } + Sink get layoutUpdate => _layout.sink; + + SettingsBloc() {} +} + +class EventProvider extends InheritedWidget { + final SettingsBloc bloc; + + EventProvider({ + Key key, + Widget child, + @required SettingsBloc bloc, + }) : bloc = bloc, + super(key: key, child: child); + + @override + bool updateShouldNotify(InheritedWidget oldWidget) => true; + + static SettingsBloc of(BuildContext context) => + (context.inheritFromWidgetOfExactType(EventProvider) as EventProvider) + .bloc; +} diff --git a/lib/widgets/news_provider.dart b/lib/widgets/news_provider.dart index 010b76d..1dceb62 100644 --- a/lib/widgets/news_provider.dart +++ b/lib/widgets/news_provider.dart @@ -61,6 +61,9 @@ class NewsProviderState extends State { @override Widget build(context) { return widget.build( - events: _events, controller: _controller, refresh: _refresh); + events: _events, + controller: _controller, + refresh: _refresh, + ); } } diff --git a/lib/widgets/notification_item.dart b/lib/widgets/notification_item.dart new file mode 100644 index 0000000..3091f0a --- /dev/null +++ b/lib/widgets/notification_item.dart @@ -0,0 +1,94 @@ +import 'dart:core'; +import 'package:flutter/material.dart' hide Notification; +import 'package:flutter/cupertino.dart' hide Notification; +import 'package:git_flux/utils/utils.dart'; + +class NotificationItem extends StatelessWidget { + const NotificationItem({ + Key key, + @required this.item, + }) : super(key: key); + + final Notification item; + + Widget _buildRoute(Notification item) { + String type = item.subject.type; + switch (type) { + case 'Issue': + case 'PullRequest': + // return IssueScreen(item.repository.); + default: + throw new Exception('Unhandled notification type: $type'); + } + } + + IconData _buildIconData(String type) { + switch (type) { + case 'Issue': + return Octicons.issue_opened; + // color: Color.fromRGBO(0x28, 0xa7, 0x45, 1), + case 'PullRequest': + return Octicons.git_pull_request; + // color: Color.fromRGBO(0x6f, 0x42, 0xc1, 1), + default: + throw new Exception('Unhandled icon type: $type'); + } + } + + @override + Widget build(BuildContext context) { + return Material( + child: InkWell( + // splashColor: Colors.transparent, + onTap: () { + Navigator.of(context).push( + CupertinoPageRoute(builder: (context) => _buildRoute(item)), + ); + }, + child: Container( + child: Row( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Icon(_buildIconData(item.subject.type), + color: CupertinoColors.inactiveGray), + ), + Expanded( + child: Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: Colors.grey))), + child: Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.subject.title, + style: TextStyle(height: 1)), + Padding(padding: EdgeInsets.only(top: 4)), + Text(TimeAgo.format(item.updatedAt), + style: TextStyle(fontSize: 12)) + ], + ), + ), + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Icon( + Octicons.check, + color: CupertinoColors.inactiveGray, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index f6223dd..097492c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,10 +15,6 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 http: ^0.11.3 rxdart: ^0.20.0 uri: ^0.11.3 @@ -30,7 +26,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - build_runner: ^1.1.3 json_serializable: ^2.0.1