Skip to content

store: Move PerAccountStoreWidget down into individual routes #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 54 additions & 54 deletions lib/widgets/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import '../model/store.dart';
import 'compose_box.dart';
import 'message_list.dart';
import 'page.dart';
import 'store.dart';

class ZulipApp extends StatelessWidget {
Expand All @@ -11,23 +12,23 @@ class ZulipApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = ThemeData(
// This applies Material 3's color system to produce a palette of
// appropriately matching and contrasting colors for use in a UI.
// The Zulip brand color is a starting point, but doesn't end up as
// one that's directly used. (After all, we didn't design it for that
// purpose; we designed a logo.) See docs:
// https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html
// Or try this tool to see the whole palette:
// https://m3.material.io/theme-builder#/custom
colorScheme: ColorScheme.fromSeed(seedColor: kZulipBrandColor));
// This applies Material 3's color system to produce a palette of
// appropriately matching and contrasting colors for use in a UI.
// The Zulip brand color is a starting point, but doesn't end up as
// one that's directly used. (After all, we didn't design it for that
// purpose; we designed a logo.) See docs:
// https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html
// Or try this tool to see the whole palette:
// https://m3.material.io/theme-builder#/custom
colorScheme: ColorScheme.fromSeed(seedColor: kZulipBrandColor));
return GlobalStoreWidget(
child: PerAccountStoreWidget(
// Just one account for now.
accountId: LiveGlobalStore.fixtureAccountId,
child: MaterialApp(
title: 'Zulip',
theme: theme,
home: const HomePage())));
child: MaterialApp(
title: 'Zulip',
theme: theme,
home: const PerAccountStoreWidget(
// Just one account for now.
accountId: LiveGlobalStore.fixtureAccountId,
child: HomePage())));
}
}

Expand All @@ -45,36 +46,34 @@ class HomePage extends StatelessWidget {
final store = PerAccountStoreWidget.of(context);

InlineSpan bold(String text) => TextSpan(
text: text, style: const TextStyle(fontWeight: FontWeight.bold));
text: text, style: const TextStyle(fontWeight: FontWeight.bold));

return Scaffold(
appBar: AppBar(title: const Text("Home")),
body: Center(
child:
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
appBar: AppBar(title: const Text("Home")),
body: Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
DefaultTextStyle.merge(
style: const TextStyle(fontSize: 18),
child: Column(children: [
const Text('🚧 Under construction 🚧'),
const SizedBox(height: 12),
Text.rich(TextSpan(
text: 'Connected to: ',
children: [bold(store.account.realmUrl)])),
Text.rich(TextSpan(
text: 'Zulip server version: ',
children: [bold(store.zulip_version)])),
Text.rich(TextSpan(text: 'Subscribed to ', children: [
bold(store.subscriptions.length.toString()),
const TextSpan(text: ' streams'),
])),
style: const TextStyle(fontSize: 18),
child: Column(children: [
const Text('🚧 Under construction 🚧'),
const SizedBox(height: 12),
Text.rich(TextSpan(
text: 'Connected to: ',
children: [bold(store.account.realmUrl)])),
Text.rich(TextSpan(
text: 'Zulip server version: ',
children: [bold(store.zulip_version)])),
Text.rich(TextSpan(text: 'Subscribed to ', children: [
bold(store.subscriptions.length.toString()),
const TextSpan(text: ' streams'),
])),
])),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const MessageListPage())),
child: const Text("All messages"))
onPressed: () => Navigator.push(context,
MaterialAccountPageRoute(context: context, builder: (context) =>
const MessageListPage())),
child: const Text("All messages")),
])));
}
}
Expand All @@ -85,21 +84,22 @@ class MessageListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("All messages")),
body: Builder(
builder: (BuildContext context) => Center(
child: Column(children: [
MediaQuery.removePadding(
// Scaffold knows about the app bar, and so has run this
// BuildContext, which is under `body`, through
// MediaQuery.removePadding with `removeTop: true`.
context: context,
appBar: AppBar(title: const Text("All messages")),
body: Builder(
builder: (BuildContext context) => Center(
child: Column(children: [
MediaQuery.removePadding(
// Scaffold knows about the app bar, and so has run this
// BuildContext, which is under `body`, through
// MediaQuery.removePadding with `removeTop: true`.
context: context,

// The compose box pads the bottom inset.
removeBottom: true,
// The compose box pads the bottom inset.
removeBottom: true,

child: const Expanded(
child: MessageList())),
const StreamComposeBox()]))));
child: const Expanded(
child: MessageList())),
const StreamComposeBox(),
]))));
}
}
50 changes: 50 additions & 0 deletions lib/widgets/page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

import 'package:flutter/material.dart';

import 'store.dart';

mixin AccountPageRouteMixin<T> on PageRoute<T> {
int get accountId;

@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return PerAccountStoreWidget(
accountId: accountId,
child: super.buildPage(context, animation, secondaryAnimation));
}
}

class MaterialAccountPageRoute<T> extends MaterialPageRoute<T> with AccountPageRouteMixin<T> {
MaterialAccountPageRoute({
required BuildContext context,
required super.builder,
super.settings,
super.maintainState,
super.fullscreenDialog,
super.allowSnapshotting,
}) : accountId = PerAccountStoreWidget.accountIdOf(context);

@override
final int accountId;
}

class AccountPageRouteBuilder<T> extends PageRouteBuilder<T> with AccountPageRouteMixin<T> {
AccountPageRouteBuilder({
required BuildContext context,
super.settings,
required super.pageBuilder,
super.transitionsBuilder,
super.transitionDuration,
super.reverseTransitionDuration,
super.opaque,
super.barrierDismissible,
super.barrierColor,
super.barrierLabel,
super.maintainState,
super.fullscreenDialog,
super.allowSnapshotting,
}) : accountId = PerAccountStoreWidget.accountIdOf(context);

@override
final int accountId;
}
24 changes: 23 additions & 1 deletion lib/widgets/store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,38 @@ class PerAccountStoreWidget extends StatefulWidget {
/// [BuildContext.dependOnInheritedWidgetOfExactType].
///
/// See also:
/// * [InheritedNotifier], which provides the "dependency" mechanism.
/// * [accountIdOf], for the account ID corresponding to the same data.
/// * [GlobalStoreWidget.of], for the app's data beyond that of a
/// particular account.
/// * [InheritedNotifier], which provides the "dependency" mechanism.
static PerAccountStore of(BuildContext context) {
final widget = context
.dependOnInheritedWidgetOfExactType<_PerAccountStoreInheritedWidget>();
assert(widget != null, 'No PerAccountStoreWidget ancestor');
return widget!.store;
}

/// Our account ID for the relevant account for this widget.
///
/// As with [of], the data is taken from the closest [PerAccountStoreWidget]
/// that encloses the given context. Throws an error if there is no enclosing
/// [PerAccountStoreWidget].
///
/// Unlike [of], this method does not create a dependency relationship, and
/// updates to the [PerAccountStoreWidget] will not cause the calling widget
/// to be rebuilt. As a result, this should not be called from build methods,
/// but is appropriate to use in interaction event handlers. For more, see
/// [BuildContext.findAncestorWidgetOfExactType].
///
/// Like [of], the cost of this method is O(1) with a small constant factor.
static int accountIdOf(BuildContext context) {
final element = context.getElementForInheritedWidgetOfExactType<_PerAccountStoreInheritedWidget>();
assert(element != null, 'No PerAccountStoreWidget ancestor');
final widget = element!.findAncestorWidgetOfExactType<PerAccountStoreWidget>();
assert(widget != null);
return widget!.accountId;
}

@override
State<PerAccountStoreWidget> createState() => _PerAccountStoreWidgetState();
}
Expand Down
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: async
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.10.0"
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
Expand Down