Skip to content

Commit

Permalink
Merge pull request #121 from rodydavis/feature/flutter-signals
Browse files Browse the repository at this point in the history
v4
  • Loading branch information
rodydavis authored Feb 5, 2024
2 parents f38a04f + 3c0c389 commit 178bfe7
Show file tree
Hide file tree
Showing 226 changed files with 9,565 additions and 1,201 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ jobs:

- run: cd packages/signals_core && dart pub get
- run: cd packages/signals_core && dart test
- run: cd packages/signals_flutter && flutter pub get
- run: cd packages/signals_flutter && flutter test
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"Buchheim",
"colorpicker",
"computeds",
"Finalizer",
"Fruchterman",
Expand All @@ -12,6 +13,7 @@
"preact",
"Reingold",
"relayout",
"rexecute",
"scrollables",
"Tianguang",
"Todos",
Expand Down
153 changes: 102 additions & 51 deletions examples/auth_flow/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,67 @@ import 'package:go_router/go_router.dart';
import 'package:signals/signals_flutter.dart';

typedef User = ({int id, String name});
typedef Setting = ({int userId, String key, bool value});

class Auth {
/// Current user signal
final currentUser = signal<User?>(null);

/// Computed signal that only emits when the user is logged in / out
late final isLoggedIn = computed(() => currentUser() != null);

/// Computed signal that returns the current user name or 'N/A'
late final currentUserName = computed(() => currentUser()?.name ?? 'N/A');

class ServerApi {
// This uses a controller but this user stream could come from a
// database or library like Firebase
final _controller = StreamController<User?>();

// Listen to auth state changes and update the current user
late Connect<User?> _authListener;

Auth() {
// Listen to the stream and update the current user
_authListener = connect(currentUser) << _controller.stream;
}
Stream<User?> userStream() => _controller.stream;

// Dispose of the stream controller
void dispose() {
_authListener.dispose();
Stream<List<Setting>> todosStream(int userId) {
return Stream.value([
(userId: userId, key: 'darkMode', value: true),
(userId: userId, key: 'notifications', value: false),
]);
}

/// Login with user data
void login(User data) {
_controller.add(data);
}

/// Logout
void logout() {
_controller.add(null);
}

void dispose() {
_controller.close();
}
}

class Auth {
final api = ServerApi();

/// Current user signal
late final currentUser = api.userStream().toSignal();

late final settings = streamSignal(
() => api.todosStream(currentUser().value?.id ?? 0),
dependencies: [currentUser],
);

/// Computed signal that only emits when the user is logged in / out
late final isLoggedIn = computed(
() => currentUser().value != null,
);

/// Computed signal that returns the current user name or 'N/A'
late final currentUserName = computed(
() => currentUser().value?.name ?? 'N/A',
);

// Dispose of the stream controller
void dispose() {
currentUser.dispose();
api.dispose();
}

/// Login with user data
void login(User data) => api.login(data);

/// Logout
void logout() => api.logout();
}

final auth = Auth();
Expand All @@ -52,7 +76,7 @@ final router = GoRouter(
GoRoute(
path: '/',
redirect: (context, state) {
if (auth.currentUser.peek() == null) return '/login';
if (auth.currentUser().value == null) return '/login';
return null;
},
builder: (context, state) => const HomeScreen(),
Expand Down Expand Up @@ -210,6 +234,7 @@ class LoginScreen extends StatelessWidget {
},
child: const Text('Login'),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
context.go('/register');
Expand Down Expand Up @@ -244,6 +269,7 @@ class RegisterScreen extends StatelessWidget {
},
child: const Text('Register'),
),
const SizedBox(height: 10),
TextButton(
onPressed: () {
context.go('/login');
Expand All @@ -262,35 +288,60 @@ class ProfileScreen extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile Screen'),
actions: [
const DarkModeToggle(),
IconButton(
icon: const Icon(Icons.logout),
tooltip: 'Logout',
onPressed: () {
auth.logout();
context.go('/login');
},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Profile'),
Watch((context) {
return Text(
auth.currentUserName(),
style: Theme.of(context).textTheme.headlineMedium,
);
}),
return Watch((context) {
return Scaffold(
appBar: AppBar(
title: Text('Profile: ${auth.currentUserName()}'),
actions: [
const DarkModeToggle(),
IconButton(
icon: const Icon(Icons.logout),
tooltip: 'Logout',
onPressed: () {
auth.logout();
context.go('/login');
},
),
],
),
),
);
body: auth.settings().map(
data: (settings) {
if (settings.isEmpty) {
return const Center(child: Text('No settings found'));
}
return ListView.builder(
itemCount: settings.length,
itemBuilder: (context, index) {
final setting = settings[index];
return ListTile(
trailing: Text(setting.userId.toString()),
title: Text(setting.key),
subtitle: Text(setting.value.toString()),
);
},
);
},
error: (e, s) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Error loading settings'),
ElevatedButton(
onPressed: () {
auth.settings.refresh();
},
child: const Text('Retry'),
),
],
),
);
},
loading: () {
return const Center(child: CircularProgressIndicator());
},
),
);
});
}
}
60 changes: 17 additions & 43 deletions examples/auth_flow/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "12.1.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739"
url: "https://pub.dev"
source: hosted
version: "9.0.16"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff
url: "https://pub.dev"
source: hosted
version: "1.0.5"
lints:
dependency: transitive
description:
Expand Down Expand Up @@ -132,18 +116,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
version: "0.5.0"
meta:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.10.0"
path:
dependency: transitive
description:
Expand All @@ -158,23 +142,21 @@ packages:
path: "../../packages/signals"
relative: true
source: path
version: "2.1.10"
version: "3.0.0"
signals_core:
dependency: transitive
dependency: "direct overridden"
description:
name: signals_core
sha256: "96ca555b5881d1b7a7b521cec9ac64c56eacc75b54a79a76e73eb0d7bcbad82e"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
path: "../../packages/signals_core"
relative: true
source: path
version: "3.0.0"
signals_flutter:
dependency: transitive
dependency: "direct overridden"
description:
name: signals_flutter
sha256: ab0e7376fd4b1fbf2f427d431bcf35a1eb2251308feb35f7b770b6f95b5677e6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
path: "../../packages/signals_flutter"
relative: true
source: path
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
Expand Down Expand Up @@ -236,22 +218,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
web:
dependency: transitive
description:
name: web
sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.4.0"
version: "0.3.0"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.7.0"
Loading

0 comments on commit 178bfe7

Please sign in to comment.