Skip to content

Commit 263dd47

Browse files
authored
Merge pull request #257 from powersync-ja/demo-state-refactoring
Demos: Replace `setState` with `StreamBuilder` where appropriate
2 parents eb0937a + 264f012 commit 263dd47

File tree

12 files changed

+188
-380
lines changed

12 files changed

+188
-380
lines changed

Diff for: demos/django-todolist/lib/main.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const listsPage = ListsPage();
4141
const homePage = listsPage;
4242

4343
const sqlConsolePage = Scaffold(
44-
appBar: StatusAppBar(title: 'SQL Console'),
44+
appBar: StatusAppBar(title: Text('SQL Console')),
4545
body: QueryWidget(defaultQuery: defaultQuery));
4646

4747
const loginPage = LoginPage();
@@ -76,7 +76,7 @@ class MyHomePage extends StatelessWidget {
7676
@override
7777
Widget build(BuildContext context) {
7878
return Scaffold(
79-
appBar: StatusAppBar(title: title),
79+
appBar: StatusAppBar(title: Text(title)),
8080
body: Center(child: content),
8181
floatingActionButton: floatingActionButton,
8282
drawer: Drawer(

Diff for: demos/django-todolist/lib/widgets/lists_page.dart

+23-40
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import 'dart:async';
2-
31
import 'package:flutter/material.dart';
42

3+
import '../powersync.dart';
54
import './list_item.dart';
65
import './list_item_dialog.dart';
76
import '../main.dart';
@@ -41,48 +40,32 @@ class ListsPage extends StatelessWidget {
4140
}
4241
}
4342

44-
class ListsWidget extends StatefulWidget {
43+
class ListsWidget extends StatelessWidget {
4544
const ListsWidget({super.key});
4645

47-
@override
48-
State<StatefulWidget> createState() {
49-
return _ListsWidgetState();
50-
}
51-
}
52-
53-
class _ListsWidgetState extends State<ListsWidget> {
54-
List<TodoList> _data = [];
55-
StreamSubscription? _subscription;
56-
57-
_ListsWidgetState();
58-
59-
@override
60-
void initState() {
61-
super.initState();
62-
final stream = TodoList.watchListsWithStats();
63-
_subscription = stream.listen((data) {
64-
if (!context.mounted) {
65-
return;
66-
}
67-
setState(() {
68-
_data = data;
69-
});
70-
});
71-
}
72-
73-
@override
74-
void dispose() {
75-
super.dispose();
76-
_subscription?.cancel();
77-
}
78-
7946
@override
8047
Widget build(BuildContext context) {
81-
return ListView(
82-
padding: const EdgeInsets.symmetric(vertical: 8.0),
83-
children: _data.map((list) {
84-
return ListItemWidget(list: list);
85-
}).toList(),
48+
return FutureBuilder(
49+
future: db.waitForFirstSync(),
50+
builder: (context, snapshot) {
51+
return switch (snapshot.connectionState) {
52+
ConnectionState.done => StreamBuilder(
53+
stream: TodoList.watchListsWithStats(),
54+
builder: (context, snapshot) {
55+
final items = snapshot.data ?? const [];
56+
57+
return ListView(
58+
padding: const EdgeInsets.symmetric(vertical: 8.0),
59+
children: items.map((list) {
60+
return ListItemWidget(list: list);
61+
}).toList(),
62+
);
63+
},
64+
),
65+
// waitForFirstSync() did not complete yet
66+
_ => const Text('Busy with sync...'),
67+
};
68+
},
8669
);
8770
}
8871
}

Diff for: demos/django-todolist/lib/widgets/status_app_bar.dart

+25-45
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,42 @@
1-
import 'dart:async';
2-
31
import 'package:flutter/foundation.dart';
42
import 'package:flutter/material.dart';
53
import 'package:powersync/powersync.dart';
64
import 'package:powersync_django_todolist_demo/widgets/fts_search_delegate.dart';
75
import '../powersync.dart';
86

9-
class StatusAppBar extends StatefulWidget implements PreferredSizeWidget {
10-
const StatusAppBar({super.key, required this.title});
11-
12-
final String title;
7+
class StatusAppBar extends StatelessWidget implements PreferredSizeWidget {
8+
final Widget title;
139

14-
@override
15-
State<StatusAppBar> createState() => _StatusAppBarState();
10+
const StatusAppBar({super.key, required this.title});
1611

1712
@override
1813
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
19-
}
20-
21-
class _StatusAppBarState extends State<StatusAppBar> {
22-
late SyncStatus _connectionState;
23-
StreamSubscription<SyncStatus>? _syncStatusSubscription;
24-
25-
@override
26-
void initState() {
27-
super.initState();
28-
_connectionState = db.currentStatus;
29-
_syncStatusSubscription = db.statusStream.listen((event) {
30-
setState(() {
31-
_connectionState = db.currentStatus;
32-
});
33-
});
34-
}
35-
36-
@override
37-
void dispose() {
38-
super.dispose();
39-
_syncStatusSubscription?.cancel();
40-
}
4114

4215
@override
4316
Widget build(BuildContext context) {
44-
final statusIcon = _getStatusIcon(_connectionState);
17+
return StreamBuilder(
18+
stream: db.statusStream,
19+
initialData: db.currentStatus,
20+
builder: (context, snapshot) {
21+
final status = snapshot.data!;
22+
final statusIcon = _getStatusIcon(status);
4523

46-
return AppBar(
47-
title: Text(widget.title),
48-
actions: <Widget>[
49-
IconButton(
50-
onPressed: () {
51-
showSearch(context: context, delegate: FtsSearchDelegate());
52-
},
53-
icon: const Icon(Icons.search),
54-
),
55-
statusIcon,
56-
// Make some space for the "Debug" banner, so that the status
57-
// icon isn't hidden
58-
if (kDebugMode) _makeIcon('Debug mode', Icons.developer_mode),
59-
],
24+
return AppBar(
25+
title: title,
26+
actions: <Widget>[
27+
IconButton(
28+
onPressed: () {
29+
showSearch(context: context, delegate: FtsSearchDelegate());
30+
},
31+
icon: const Icon(Icons.search),
32+
),
33+
statusIcon,
34+
// Make some space for the "Debug" banner, so that the status
35+
// icon isn't hidden
36+
if (kDebugMode) _makeIcon('Debug mode', Icons.developer_mode),
37+
],
38+
);
39+
},
6040
);
6141
}
6242
}
+14-42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import 'dart:async';
2-
31
import 'package:flutter/material.dart';
4-
import 'package:powersync_django_todolist_demo/models/todo_item.dart';
52

63
import './status_app_bar.dart';
74
import './todo_item_dialog.dart';
@@ -34,56 +31,31 @@ class TodoListPage extends StatelessWidget {
3431
);
3532

3633
return Scaffold(
37-
appBar: StatusAppBar(title: list.name),
34+
appBar: StatusAppBar(title: Text(list.name)),
3835
floatingActionButton: button,
3936
body: TodoListWidget(list: list));
4037
}
4138
}
4239

43-
class TodoListWidget extends StatefulWidget {
40+
class TodoListWidget extends StatelessWidget {
4441
final TodoList list;
4542

4643
const TodoListWidget({super.key, required this.list});
4744

48-
@override
49-
State<StatefulWidget> createState() {
50-
return TodoListWidgetState();
51-
}
52-
}
53-
54-
class TodoListWidgetState extends State<TodoListWidget> {
55-
List<TodoItem> _data = [];
56-
StreamSubscription? _subscription;
57-
58-
TodoListWidgetState();
59-
60-
@override
61-
void initState() {
62-
super.initState();
63-
final stream = widget.list.watchItems();
64-
_subscription = stream.listen((data) {
65-
if (!context.mounted) {
66-
return;
67-
}
68-
setState(() {
69-
_data = data;
70-
});
71-
});
72-
}
73-
74-
@override
75-
void dispose() {
76-
super.dispose();
77-
_subscription?.cancel();
78-
}
79-
8045
@override
8146
Widget build(BuildContext context) {
82-
return ListView(
83-
padding: const EdgeInsets.symmetric(vertical: 8.0),
84-
children: _data.map((todo) {
85-
return TodoItemWidget(todo: todo);
86-
}).toList(),
47+
return StreamBuilder(
48+
stream: list.watchItems(),
49+
builder: (context, snapshot) {
50+
final items = snapshot.data ?? const [];
51+
52+
return ListView(
53+
padding: const EdgeInsets.symmetric(vertical: 8.0),
54+
children: items.map((todo) {
55+
return TodoItemWidget(todo: todo);
56+
}).toList(),
57+
);
58+
},
8759
);
8860
}
8961
}

Diff for: demos/firebase-nodejs-todolist/lib/main.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const listsPage = ListsPage();
4040
const homePage = listsPage;
4141

4242
const sqlConsolePage = Scaffold(
43-
appBar: StatusAppBar(title: 'SQL Console'),
43+
appBar: StatusAppBar(title: Text('SQL Console')),
4444
body: QueryWidget(defaultQuery: defaultQuery));
4545

4646
const loginPage = LoginPage();
@@ -77,7 +77,7 @@ class MyHomePage extends StatelessWidget {
7777
@override
7878
Widget build(BuildContext context) {
7979
return Scaffold(
80-
appBar: StatusAppBar(title: title),
80+
appBar: StatusAppBar(title: Text(title)),
8181
body: Center(child: content),
8282
floatingActionButton: floatingActionButton,
8383
drawer: Drawer(
+23-40
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import 'dart:async';
2-
31
import 'package:flutter/material.dart';
42

3+
import '../powersync.dart';
54
import './list_item.dart';
65
import './list_item_dialog.dart';
76
import '../main.dart';
@@ -41,48 +40,32 @@ class ListsPage extends StatelessWidget {
4140
}
4241
}
4342

44-
class ListsWidget extends StatefulWidget {
43+
class ListsWidget extends StatelessWidget {
4544
const ListsWidget({super.key});
4645

47-
@override
48-
State<StatefulWidget> createState() {
49-
return _ListsWidgetState();
50-
}
51-
}
52-
53-
class _ListsWidgetState extends State<ListsWidget> {
54-
List<TodoList> _data = [];
55-
StreamSubscription? _subscription;
56-
57-
_ListsWidgetState();
58-
59-
@override
60-
void initState() {
61-
super.initState();
62-
final stream = TodoList.watchListsWithStats();
63-
_subscription = stream.listen((data) {
64-
if (!mounted) {
65-
return;
66-
}
67-
setState(() {
68-
_data = data;
69-
});
70-
});
71-
}
72-
73-
@override
74-
void dispose() {
75-
super.dispose();
76-
_subscription?.cancel();
77-
}
78-
7946
@override
8047
Widget build(BuildContext context) {
81-
return ListView(
82-
padding: const EdgeInsets.symmetric(vertical: 8.0),
83-
children: _data.map((list) {
84-
return ListItemWidget(list: list);
85-
}).toList(),
48+
return FutureBuilder(
49+
future: db.waitForFirstSync(),
50+
builder: (context, snapshot) {
51+
return switch (snapshot.connectionState) {
52+
ConnectionState.done => StreamBuilder(
53+
stream: TodoList.watchListsWithStats(),
54+
builder: (context, snapshot) {
55+
final items = snapshot.data ?? const [];
56+
57+
return ListView(
58+
padding: const EdgeInsets.symmetric(vertical: 8.0),
59+
children: items.map((list) {
60+
return ListItemWidget(list: list);
61+
}).toList(),
62+
);
63+
},
64+
),
65+
// waitForFirstSync() did not complete yet
66+
_ => const Text('Busy with sync...'),
67+
};
68+
},
8669
);
8770
}
8871
}

0 commit comments

Comments
 (0)