refactor: use bloc pattern to manage state
This commit is contained in:
parent
a051a54549
commit
0a1e65ec4f
|
@ -1,12 +1,9 @@
|
|||
// import 'dart:async';
|
||||
// import 'dart:convert';
|
||||
import '../utils.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
// import 'package:graphql_flutter/graphql_flutter.dart';
|
||||
// import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../components/event.dart';
|
||||
// import 'user.dart';
|
||||
import '../providers/event.dart';
|
||||
import '../models/event.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
|
@ -17,33 +14,30 @@ class HomeScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class HomeScreenState extends State<HomeScreen> {
|
||||
int page = 1;
|
||||
List<Event> events = [];
|
||||
// final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||
// GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
loadFirst() async {
|
||||
events = await fetchEvents();
|
||||
// print(events);
|
||||
return events;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// FIXME: context hack
|
||||
// Future.delayed(Duration(seconds: 0)).then((_) {
|
||||
// EventProvider.of(context).update.add(true);
|
||||
// });
|
||||
}
|
||||
|
||||
loadMore() async {
|
||||
events.addAll(await fetchEvents(page + 1));
|
||||
page++;
|
||||
return events;
|
||||
}
|
||||
|
||||
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||
GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
final eventBloc = EventProvider.of(context);
|
||||
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text('Home'),
|
||||
),
|
||||
child: FutureBuilder(
|
||||
future: loadFirst(),
|
||||
child: StreamBuilder<List<Event>>(
|
||||
stream: eventBloc.events,
|
||||
builder: (context, snapshot) {
|
||||
// print(snapshot.data);
|
||||
Widget widget;
|
||||
if (snapshot.hasData) {
|
||||
// List<Event> events = snapshot.data;
|
||||
|
@ -59,7 +53,7 @@ class HomeScreenState extends State<HomeScreen> {
|
|||
widget = ListView.builder(itemBuilder: (context, index) {
|
||||
// print(index);
|
||||
try {
|
||||
return EventItem(events[index]);
|
||||
return EventItem(snapshot.data[index]);
|
||||
} catch (err) {
|
||||
return Text(err.toString());
|
||||
// return null;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
import '../utils.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import '../models/notification.dart';
|
||||
import '../providers/notification.dart';
|
||||
|
||||
class NotificationScreen extends StatefulWidget {
|
||||
@override
|
||||
|
@ -9,63 +8,74 @@ class NotificationScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class NotificationScreenState extends State<NotificationScreen> {
|
||||
int tabIndex = 0;
|
||||
bool loading = false;
|
||||
List<NotificationItem> items = [];
|
||||
|
||||
initState() {
|
||||
super.initState();
|
||||
initFetch();
|
||||
}
|
||||
|
||||
initFetch() async {
|
||||
items = await fetchNotifications('all');
|
||||
setState(() {});
|
||||
// initFetch();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO: implement dispose
|
||||
super.dispose();
|
||||
print('dispose');
|
||||
}
|
||||
|
||||
_onChange(int value) async {
|
||||
setState(() {
|
||||
loading = true;
|
||||
tabIndex = value;
|
||||
});
|
||||
items = await fetchNotifications('all');
|
||||
setState(() {
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
NotificationBloc bloc = NotificationProvider.of(context);
|
||||
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
CupertinoSegmentedControl(
|
||||
groupValue: tabIndex,
|
||||
onValueChanged: _onChange,
|
||||
children: {
|
||||
0: Text('Unread'),
|
||||
1: Text('Paticipating'),
|
||||
2: Text('All')
|
||||
StreamBuilder<int>(
|
||||
stream: bloc.active,
|
||||
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')
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
Flexible(
|
||||
child: loading
|
||||
? CupertinoActivityIndicator()
|
||||
: ListView.builder(
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) => RichText(
|
||||
text: TextSpan(
|
||||
text: items[index].id,
|
||||
style: TextStyle(color: CupertinoColors.black)),
|
||||
)),
|
||||
)
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -5,9 +5,15 @@ import 'package:flutter/cupertino.dart';
|
|||
// import 'android/main.dart';
|
||||
import 'ios/main.dart';
|
||||
// import 'token.dart';
|
||||
import 'providers/event.dart';
|
||||
import 'providers/notification.dart';
|
||||
|
||||
class App extends StatelessWidget {
|
||||
final isIos = true;
|
||||
final EventBloc eventBloc;
|
||||
final NotificationBloc notificationBloc;
|
||||
|
||||
App(this.eventBloc, this.notificationBloc);
|
||||
|
||||
// final ValueNotifier<GraphQLClient> client = ValueNotifier(
|
||||
// GraphQLClient(
|
||||
|
@ -21,21 +27,32 @@ class App extends StatelessWidget {
|
|||
|
||||
@override
|
||||
build(context) {
|
||||
return MaterialApp(
|
||||
title: 'GitFlux',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
return NotificationProvider(
|
||||
bloc: notificationBloc,
|
||||
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(),
|
||||
},
|
||||
),
|
||||
),
|
||||
home: DefaultTextStyle(
|
||||
style: TextStyle(color: CupertinoColors.black),
|
||||
child: IosHomePage(title: 'GitFlux'),
|
||||
),
|
||||
routes: {
|
||||
// '/notification': (context) => IosNotificationTab(),
|
||||
// '/profile': (context) => IosProfileTab(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() => runApp(App());
|
||||
void main() {
|
||||
EventBloc eventBloc = EventBloc();
|
||||
NotificationBloc notificationBloc = NotificationBloc();
|
||||
|
||||
runApp(App(eventBloc, notificationBloc));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'dart:async';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import '../models/event.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
Future<List<Event>> fetchEvents([int page = 1]) async {
|
||||
List data = await getWithCredentials(
|
||||
'/users/pd4d10/received_events/public?page=$page',
|
||||
);
|
||||
return data.map<Event>((item) {
|
||||
return Event.fromJson(item);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
class EventBloc {
|
||||
final _items = BehaviorSubject<List<Event>>(seedValue: []);
|
||||
final _page = BehaviorSubject(seedValue: 0);
|
||||
final _update = StreamController<bool>();
|
||||
|
||||
// Stream<int> get eventPage => _page.stream;
|
||||
Stream<List<Event>> get events => _items.stream;
|
||||
Sink<bool> get update => _update.sink;
|
||||
|
||||
EventBloc() {
|
||||
_update.stream.listen((bool isRefresh) async {
|
||||
if (isRefresh) {
|
||||
_items.add(await fetchEvents());
|
||||
_page.add(1);
|
||||
} else {
|
||||
_items.add(await fetchEvents());
|
||||
_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;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:rxdart/subjects.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import '../utils.dart';
|
||||
import '../models/notification.dart';
|
||||
|
||||
Future<List<NotificationItem>> fetchNotifications([int index = 0]) async {
|
||||
String search = '';
|
||||
if (index == 1) {
|
||||
search = '?paticipating=true';
|
||||
} else if (index == 2) {
|
||||
search = '?all=true';
|
||||
}
|
||||
List data = await getWithCredentials('/notifications$search');
|
||||
// print(data.length);
|
||||
return data
|
||||
.map<NotificationItem>((item) => NotificationItem.fromJson(item))
|
||||
.toList();
|
||||
}
|
||||
|
||||
class NotificationBloc {
|
||||
final _items = BehaviorSubject<List<NotificationItem>>(seedValue: []);
|
||||
final _active = BehaviorSubject(seedValue: 0);
|
||||
final _loading = BehaviorSubject(seedValue: false);
|
||||
|
||||
Stream<List<NotificationItem>> get items => _items.stream;
|
||||
Stream<int> get active => _active.stream;
|
||||
Stream<bool> get loading => _loading.stream;
|
||||
|
||||
Sink<int> get activeUpdate => _active.sink;
|
||||
|
||||
NotificationBloc() {
|
||||
_active.stream.listen((int index) async {
|
||||
_loading.add(true);
|
||||
_items.add(await fetchNotifications(index));
|
||||
_loading.add(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationProvider extends InheritedWidget {
|
||||
final NotificationBloc bloc;
|
||||
|
||||
NotificationProvider({
|
||||
Key key,
|
||||
Widget child,
|
||||
@required NotificationBloc bloc,
|
||||
}) : bloc = bloc,
|
||||
super(key: key, child: child);
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(InheritedWidget oldWidget) => true;
|
||||
|
||||
static NotificationBloc of(BuildContext context) =>
|
||||
(context.inheritFromWidgetOfExactType(NotificationProvider)
|
||||
as NotificationProvider)
|
||||
.bloc;
|
||||
}
|
|
@ -3,9 +3,7 @@ import 'dart:async';
|
|||
import 'dart:io';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'token.dart';
|
||||
import 'models/event.dart';
|
||||
import 'models/user.dart';
|
||||
import 'models/notification.dart';
|
||||
|
||||
final prefix = 'https://api.github.com';
|
||||
|
||||
|
@ -21,24 +19,7 @@ Future<dynamic> getWithCredentials(String url) async {
|
|||
return data;
|
||||
}
|
||||
|
||||
Future<List<Event>> fetchEvents([int page = 1]) async {
|
||||
List data = await getWithCredentials(
|
||||
'/users/pd4d10/received_events/public?page=$page',
|
||||
);
|
||||
return data.map<Event>((item) {
|
||||
return Event.fromJson(item);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<User> fetchUser(String login) async {
|
||||
Map<String, dynamic> data = await getWithCredentials('/users/$login');
|
||||
return User.fromJson(data);
|
||||
}
|
||||
|
||||
Future<List<NotificationItem>> fetchNotifications(String type) async {
|
||||
List data = await getWithCredentials('/notifications?$type=true');
|
||||
print(data);
|
||||
return data
|
||||
.map<NotificationItem>((item) => NotificationItem.fromJson(item))
|
||||
.toList();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ dependencies:
|
|||
cupertino_icons: ^0.1.2
|
||||
http: ^0.11.3
|
||||
graphql_flutter: ^1.0.0-alpha
|
||||
rxdart: ^0.20.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue