Browse Source

Merge pull request #977 from nextcloud/refactor/neon/result

docs(neon): unify and document Result and ResultBuilder
pull/1021/head
Nikolas Rimikis 1 year ago committed by GitHub
parent
commit
6ba2f91e3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/neon/neon/lib/blocs.dart
  2. 4
      packages/neon/neon/lib/src/app.dart
  3. 163
      packages/neon/neon/lib/src/bloc/result.dart
  4. 53
      packages/neon/neon/lib/src/bloc/result_builder.dart
  5. 4
      packages/neon/neon/lib/src/pages/account_settings.dart
  6. 4
      packages/neon/neon/lib/src/pages/home.dart
  7. 3
      packages/neon/neon/lib/src/pages/login_check_account.dart
  8. 3
      packages/neon/neon/lib/src/pages/login_check_server_status.dart
  9. 4
      packages/neon/neon/lib/src/pages/login_flow.dart
  10. 4
      packages/neon/neon/lib/src/widgets/account_tile.dart
  11. 6
      packages/neon/neon/lib/src/widgets/app_bar.dart
  12. 6
      packages/neon/neon/lib/src/widgets/drawer.dart
  13. 3
      packages/neon/neon/lib/src/widgets/unified_search_results.dart
  14. 1
      packages/neon/neon/lib/src/widgets/user_avatar.dart
  15. 2
      packages/neon/neon_dashboard/lib/src/pages/main.dart
  16. 2
      packages/neon/neon_files/lib/widgets/browser_view.dart
  17. 2
      packages/neon/neon_news/lib/dialogs/add_feed.dart
  18. 4
      packages/neon/neon_news/lib/widgets/articles_view.dart
  19. 4
      packages/neon/neon_news/lib/widgets/feeds_view.dart
  20. 4
      packages/neon/neon_news/lib/widgets/folders_view.dart
  21. 2
      packages/neon/neon_notes/lib/dialogs/create_note.dart
  22. 2
      packages/neon/neon_notes/lib/dialogs/select_category.dart
  23. 2
      packages/neon/neon_notes/lib/widgets/categories_view.dart
  24. 2
      packages/neon/neon_notes/lib/widgets/notes_view.dart
  25. 2
      packages/neon/neon_notifications/lib/pages/main.dart

1
packages/neon/neon/lib/blocs.dart

@ -1,6 +1,5 @@
export 'package:neon/src/bloc/bloc.dart';
export 'package:neon/src/bloc/result.dart';
export 'package:neon/src/bloc/result_builder.dart';
// TODO: Remove access to the AccountsBloc. Apps should not need to access this
export 'package:neon/src/blocs/accounts.dart' show AccountsBloc;
export 'package:neon/src/blocs/timer.dart' hide TimerBlocEvents, TimerBlocStates;

4
packages/neon/neon/lib/src/app.dart

@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_implementation.dart';
@ -283,7 +283,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
builder: (final context, final activeAccountSnapshot) {
FlutterNativeSplash.remove();
return ResultBuilder<core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data?>.behaviorSubject(
stream: activeAccountSnapshot.hasData
subject: activeAccountSnapshot.hasData
? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities
: null,
builder: (final context, final capabilitiesSnapshot) {

163
packages/neon/neon/lib/src/bloc/result.dart

@ -1,7 +1,19 @@
import 'package:meta/meta.dart';
import 'package:flutter/widgets.dart';
import 'package:rxdart/rxdart.dart';
/// Immutable representation of the most recent interaction with a data fetching
/// computation.
///
/// See also:
/// * [ResultBuilder], which builds itself based on a snapshot from interacting with a [Stream] of `Result`s.
@immutable
class Result<T> {
/// Creates a new Result.
///
/// See also:
/// * [Result.loading], for a Result in the loading state.
/// * [Result.success], for a Result in the success state.
/// * [Result.error], for a Result in the error state.
Result(
this.data,
this.error, {
@ -12,6 +24,12 @@ class Result<T> {
'Result() called without specifying the data type. Call Result<T>() instead',
);
/// Creates a new Result in the loading state.
///
/// See also:
/// * [Result], for a basic Result.
/// * [Result.success], for a Result in the success state.
/// * [Result.error], for a Result in the error state.
Result.loading()
: data = null,
error = null,
@ -22,6 +40,12 @@ class Result<T> {
'Result.loading() called without specifying the data type. Call Result<T>.loading() instead',
);
/// Creates a new Result in the success state.
///
/// See also:
/// * [Result], for a basic Result.
/// * [Result.loading], for a Result in the loading state.
/// * [Result.error], for a Result in the error state.
Result.success(this.data)
: error = null,
isLoading = false,
@ -31,6 +55,12 @@ class Result<T> {
'Result.success() called without specifying the data type. Call Result<T>.success() instead',
);
/// Creates a new Result in the error state.
///
/// See also:
/// * [Result], for a basic Result.
/// * [Result.loading], for a Result in the loading state.
/// * [Result.success], for a Result in the success state.
Result.error(this.error)
: data = null,
isLoading = false,
@ -40,18 +70,41 @@ class Result<T> {
'Result.error() called without specifying the data type. Call Result<T>.error() instead',
);
/// The latest data received by the data fetching computation.
///
/// If this is non-null, [hasData] will be `true`.
///
/// If the data fetching computation has never returned a value, this may be
/// set to an initial data value.
/// See [ResultBuilder.initialData].
final T? data;
/// The latest error object received by the data fetching computation.
///
/// A result may both have an error and data.
final Object? error;
/// Whether new [data] is being fetched right now.
///
/// A loading result may have cached data.
final bool isLoading;
/// Whether the [data] was fetched from cache.
///
/// A cached result may be in the loading state.
final bool isCached;
Result<R> transform<R>(final R? Function(T data) call) => Result(
hasData ? call(data as T) : null,
/// Transforms the subtype of the Result by applying [callback].
///
/// If the result has no data `callback` will not be called.
Result<R> transform<R>(final R? Function(T data) callback) => Result(
hasData ? callback(data as T) : null,
error,
isLoading: isLoading,
isCached: isCached,
);
/// Copies this Result in a loading state.
Result<T> asLoading() => copyWith(isLoading: true);
Result<T> copyWith({
@ -67,12 +120,26 @@ class Result<T> {
isCached: isCached ?? this.isCached,
);
/// Returns whether this snapshot contains a non-null [error] value.
///
/// A result may both have an error and data.
bool get hasError => error != null;
/// Returns whether this snapshot contains a non-null [data] value.
///
/// This can be false even when the asynchronous computation has completed
/// successfully, if the computation did not return a non-null value. For
/// example, a [Future<void>] will complete with the null value even if it
/// completes successfully.
/// A result may both have an error and data.
bool get hasData => data != null;
/// Returns whether this snapshot [hasData] and [isCached] is not true.
bool get hasUncachedData => hasData && !isCached;
/// Returns the latest data received, failing if there is no data.
///
/// Throws a [StateError], if no data is present.
T get requireData {
if (hasData) {
return data!;
@ -88,3 +155,93 @@ class Result<T> {
@override
int get hashCode => Object.hash(data, error, isLoading, isCached);
}
/// Signature for strategies that build widgets based on asynchronous [Result]s.
///
/// See also:
/// * [ResultBuilder], which delegates to an [ResultWidgetBuilder] to build
/// itself based on [Result] events from a [Stream].
typedef ResultWidgetBuilder<T> = Widget Function(BuildContext context, Result<T> snapshot);
/// Widget that builds itself based on the latest snapshot of interaction with
/// a [Stream<Result>].
///
/// Widget rebuilding is scheduled by each interaction, using [State.setState],
/// but is otherwise decoupled from the timing of the stream. The [builder]
/// is called at the discretion of the Flutter pipeline, and will thus receive a
/// timing-dependent sub-sequence of the snapshots that represent the
/// interaction with the stream.
///
/// The initial snapshot data can be controlled by specifying [initialData].
/// This should be used to ensure that the first frame has the expected value,
/// as the builder will always be called before the stream listener has a chance
/// to be processed.
class ResultBuilder<T> extends StreamBuilderBase<Result<T>, Result<T>> {
/// Creates a new result stream builder.
///
/// See also:
/// * [ResultBuilder.behaviorSubject] for automatically setting the initial
/// data from a [BehaviorSubject].
const ResultBuilder({
required this.builder,
this.initialData,
super.stream,
super.key,
});
/// Creates a new result stream builder for a [BehaviorSubject].
///
/// The [initialData] will be set to the current value of the subject.
ResultBuilder.behaviorSubject({
required this.builder,
final BehaviorSubject<Result<T>>? subject,
super.key,
}) : initialData = subject?.valueOrNull,
super(stream: subject);
/// Builder function called with the current [Result] value.
///
/// This builder must only return a widget and should not have any side
/// effects as it may be called multiple times.
final ResultWidgetBuilder<T> builder;
/// The data that will be used to create the initial result.
///
/// Providing this value (presumably obtained synchronously when the [Stream]
/// was created) ensures that the first frame will show useful data.
/// Otherwise, the first frame will be built with the value null, regardless
/// of whether a value is available on the stream: since streams are
/// asynchronous, no events from the stream can be obtained before the initial
/// build.
final Result<T>? initialData;
@override
Result<T> initial() => initialData?.asLoading() ?? Result<T>.loading();
@override
Result<T> afterData(final Result<T> current, final Result<T> data) {
// prevent rebuild when only the cache state changes
if (current == data) {
return current;
}
return data;
}
@override
Result<T> afterError(final Result<T> current, final Object error, final StackTrace stackTrace) {
if (current.hasError) {
return current;
}
return Result(
current.data,
error,
isLoading: false,
isCached: false,
);
}
@override
Widget build(final BuildContext context, final Result<T> currentSummary) => builder(context, currentSummary);
}

53
packages/neon/neon/lib/src/bloc/result_builder.dart

@ -1,53 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:rxdart/rxdart.dart';
typedef ResultWidgetBuilder<T> = Widget Function(BuildContext context, Result<T> snapshot);
class ResultBuilder<T> extends StreamBuilderBase<Result<T>, Result<T>> {
const ResultBuilder({
required this.builder,
this.initialData,
super.stream,
super.key,
});
ResultBuilder.behaviorSubject({
required this.builder,
BehaviorSubject<Result<T>>? super.stream,
super.key,
}) : initialData = stream?.valueOrNull;
final ResultWidgetBuilder<T> builder;
final Result<T>? initialData;
@override
Result<T> initial() => initialData?.asLoading() ?? Result<T>.loading();
@override
Result<T> afterData(final Result<T> current, final Result<T> data) {
// prevent rebuild when only the cache state changes
if (current == data) {
return current;
}
return data;
}
@override
Result<T> afterError(final Result<T> current, final Object error, final StackTrace stackTrace) {
if (current.hasError) {
return current;
}
return Result(
current.data,
error,
isLoading: false,
isCached: false,
);
}
@override
Widget build(final BuildContext context, final Result<T> currentSummary) => builder(context, currentSummary);
}

4
packages/neon/neon/lib/src/pages/account_settings.dart

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/router.dart';
@ -78,7 +78,7 @@ class AccountSettingsPage extends StatelessWidget {
);
final body = ResultBuilder<provisioning_api.UserDetails>.behaviorSubject(
stream: userDetailsBloc.userDetails,
subject: userDetailsBloc.userDetails,
builder: (final context, final userDetails) {
final quotaRelative = userDetails.data?.quota.relative?.$int ?? userDetails.data?.quota.relative?.$double ?? 0;
final quotaTotal = userDetails.data?.quota.total?.$int ?? userDetails.data?.quota.total?.$double ?? 0;

4
packages/neon/neon/lib/src/pages/home.dart

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/apps.dart';
import 'package:neon/src/models/account.dart';
@ -133,7 +133,7 @@ class _HomePageState extends State<HomePage> {
return const NeonUnifiedSearchResults();
}
return ResultBuilder<Iterable<AppImplementation>>.behaviorSubject(
stream: _appsBloc.appImplementations,
subject: _appsBloc.appImplementations,
builder: (final context, final appImplementations) {
if (!appImplementations.hasData) {
return const SizedBox();

3
packages/neon/neon/lib/src/pages/login_check_account.dart

@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/login_check_account.dart';
import 'package:neon/src/models/account.dart';
@ -61,7 +60,7 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
stream: bloc.state,
subject: bloc.state,
builder: (final context, final state) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [

3
packages/neon/neon/lib/src/pages/login_check_server_status.dart

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/login_check_server_status.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/theme/dialog.dart';
@ -58,7 +57,7 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
stream: bloc.state,
subject: bloc.state,
builder: (final context, final state) {
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance;

4
packages/neon/neon/lib/src/pages/login_flow.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/login_flow.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/widgets/error.dart';
@ -61,7 +61,7 @@ class _LoginFlowPageState extends State<LoginFlowPage> {
child: Padding(
padding: const EdgeInsets.all(10),
child: ResultBuilder.behaviorSubject(
stream: bloc.init,
subject: bloc.init,
builder: (final context, final init) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [

4
packages/neon/neon/lib/src/widgets/account_tile.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:intersperse/intersperse.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/utils/provider.dart';
@ -37,7 +37,7 @@ class NeonAccountTile extends StatelessWidget {
),
trailing: trailing,
title: ResultBuilder<provisioning_api.UserDetails>.behaviorSubject(
stream: userDetailsBloc.userDetails,
subject: userDetailsBloc.userDetails,
builder: (final context, final userDetails) => Row(
children: [
if (userDetails.hasData)

6
packages/neon/neon/lib/src/widgets/app_bar.dart

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/apps.dart';
import 'package:neon/src/models/account.dart';
@ -63,7 +63,7 @@ class _NeonAppBarState extends State<NeonAppBar> {
@override
Widget build(final BuildContext context) => ResultBuilder<Iterable<AppImplementation>>.behaviorSubject(
stream: appsBloc.appImplementations,
subject: appsBloc.appImplementations,
builder: (final context, final appImplementations) => StreamBuilder(
stream: appsBloc.activeApp,
builder: (final context, final activeAppSnapshot) => StreamBuilder(
@ -242,7 +242,7 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
@override
Widget build(final BuildContext context) => ResultBuilder<NotificationsAppInterface?>.behaviorSubject(
stream: _appsBloc.notificationsAppImplementation,
subject: _appsBloc.notificationsAppImplementation,
builder: (final context, final notificationsAppImplementation) {
if (!notificationsAppImplementation.hasData) {
return const SizedBox.shrink();

6
packages/neon/neon/lib/src/widgets/drawer.dart

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/apps.dart';
import 'package:neon/src/models/app_implementation.dart';
@ -27,7 +27,7 @@ class NeonDrawer extends StatelessWidget {
final appsBloc = accountsBloc.activeAppsBloc;
return ResultBuilder.behaviorSubject(
stream: appsBloc.appImplementations,
subject: appsBloc.appImplementations,
builder: (final context, final snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
@ -122,7 +122,7 @@ class NeonDrawerHeader extends StatelessWidget {
final capabilitiesBloc = accountsBloc.activeCapabilitiesBloc;
final branding = ResultBuilder<core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data>.behaviorSubject(
stream: capabilitiesBloc.capabilities,
subject: capabilitiesBloc.capabilities,
builder: (final context, final capabilities) {
if (!capabilities.hasData) {
return NeonLinearProgressIndicator(

3
packages/neon/neon/lib/src/widgets/unified_search_results.dart

@ -3,7 +3,6 @@ import 'package:go_router/go_router.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/blocs/unified_search.dart';
import 'package:neon/src/models/account.dart';
@ -27,7 +26,7 @@ class NeonUnifiedSearchResults extends StatelessWidget {
final accountsBloc = NeonProvider.of<AccountsBloc>(context);
final bloc = accountsBloc.activeUnifiedSearchBloc;
return ResultBuilder.behaviorSubject(
stream: bloc.results,
subject: bloc.results,
builder: (final context, final results) {
final values = results.data?.entries.toList();

1
packages/neon/neon/lib/src/widgets/user_avatar.dart

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/theme/sizes.dart';

2
packages/neon/neon_dashboard/lib/src/pages/main.dart

@ -17,7 +17,7 @@ class DashboardMainPage extends StatelessWidget {
final bloc = NeonProvider.of<DashboardBloc>(context);
return ResultBuilder.behaviorSubject(
stream: bloc.widgets,
subject: bloc.widgets,
builder: (final context, final snapshot) {
Widget? child;
if (snapshot.hasData) {

2
packages/neon/neon_files/lib/widgets/browser_view.dart

@ -42,7 +42,7 @@ class _FilesBrowserViewState extends State<FilesBrowserView> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<WebDavFile>>.behaviorSubject(
stream: widget.bloc.files,
subject: widget.bloc.files,
builder: (final context, final filesSnapshot) => StreamBuilder<List<String>>(
stream: widget.bloc.path,
builder: (final context, final pathSnapshot) => StreamBuilder<List<FilesTask>>(

2
packages/neon/neon_news/lib/dialogs/add_feed.dart

@ -50,7 +50,7 @@ class _NewsAddFeedDialogState extends State<NewsAddFeedDialog> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<news.Folder>>.behaviorSubject(
stream: widget.bloc.folders,
subject: widget.bloc.folders,
builder: (final context, final folders) => NeonDialog(
title: Text(NewsLocalizations.of(context).feedAdd),
children: [

4
packages/neon/neon_news/lib/widgets/articles_view.dart

@ -26,9 +26,9 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<news.Feed>>.behaviorSubject(
stream: widget.newsBloc.feeds,
subject: widget.newsBloc.feeds,
builder: (final context, final feeds) => ResultBuilder<List<news.Article>>.behaviorSubject(
stream: widget.bloc.articles,
subject: widget.bloc.articles,
builder: (final context, final articles) => SortBoxBuilder<ArticlesSortProperty, news.Article>(
sortBox: articlesSortBox,
sortProperty: widget.newsBloc.options.articlesSortPropertyOption,

4
packages/neon/neon_news/lib/widgets/feeds_view.dart

@ -12,9 +12,9 @@ class NewsFeedsView extends StatelessWidget {
@override
Widget build(final BuildContext context) => ResultBuilder<List<news.Folder>>.behaviorSubject(
stream: bloc.folders,
subject: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<news.Feed>>.behaviorSubject(
stream: bloc.feeds,
subject: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FeedsSortProperty, news.Feed>(
sortBox: feedsSortBox,
sortProperty: bloc.options.feedsSortPropertyOption,

4
packages/neon/neon_news/lib/widgets/folders_view.dart

@ -10,9 +10,9 @@ class NewsFoldersView extends StatelessWidget {
@override
Widget build(final BuildContext context) => ResultBuilder<List<news.Folder>>.behaviorSubject(
stream: bloc.folders,
subject: bloc.folders,
builder: (final context, final folders) => ResultBuilder<List<news.Feed>>.behaviorSubject(
stream: bloc.feeds,
subject: bloc.feeds,
builder: (final context, final feeds) => SortBoxBuilder<FoldersSortProperty, FolderFeedsWrapper>(
sortBox: foldersSortBox,
sortProperty: bloc.options.foldersSortPropertyOption,

2
packages/neon/neon_notes/lib/dialogs/create_note.dart

@ -33,7 +33,7 @@ class _NotesCreateNoteDialogState extends State<NotesCreateNoteDialog> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
stream: widget.bloc.notesList,
subject: widget.bloc.notesList,
builder: (final context, final notes) => NeonDialog(
title: Text(NotesLocalizations.of(context).noteCreate),
children: [

2
packages/neon/neon_notes/lib/dialogs/select_category.dart

@ -27,7 +27,7 @@ class _NotesSelectCategoryDialogState extends State<NotesSelectCategoryDialog> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
stream: widget.bloc.notesList,
subject: widget.bloc.notesList,
builder: (final context, final notes) => NeonDialog(
title: Text(NotesLocalizations.of(context).category),
children: [

2
packages/neon/neon_notes/lib/widgets/categories_view.dart

@ -10,7 +10,7 @@ class NotesCategoriesView extends StatelessWidget {
@override
Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
stream: bloc.notesList,
subject: bloc.notesList,
builder: (final context, final notes) => SortBoxBuilder<CategoriesSortProperty, NoteCategory>(
sortBox: categoriesSortBox,
sortProperty: bloc.options.categoriesSortPropertyOption,

2
packages/neon/neon_notes/lib/widgets/notes_view.dart

@ -12,7 +12,7 @@ class NotesView extends StatelessWidget {
@override
Widget build(final BuildContext context) => ResultBuilder<List<notes.Note>>.behaviorSubject(
stream: bloc.notesList,
subject: bloc.notesList,
builder: (final context, final notesList) => SortBoxBuilder<NotesSortProperty, notes.Note>(
sortBox: notesSortBox,
presort: const {

2
packages/neon/neon_notifications/lib/pages/main.dart

@ -25,7 +25,7 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
@override
Widget build(final BuildContext context) => ResultBuilder<List<notifications.Notification>>.behaviorSubject(
stream: bloc.notificationsList,
subject: bloc.notificationsList,
builder: (final context, final notifications) => Scaffold(
resizeToAvoidBottomInset: false,
floatingActionButton: StreamBuilder<int>(

Loading…
Cancel
Save