diff --git a/packages/neon/neon/lib/blocs.dart b/packages/neon/neon/lib/blocs.dart index c7c95a68..820fbc22 100644 --- a/packages/neon/neon/lib/blocs.dart +++ b/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; diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 5513f751..42a61086 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/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 with WidgetsBindingObserver, tray.Tra builder: (final context, final activeAccountSnapshot) { FlutterNativeSplash.remove(); return ResultBuilder.behaviorSubject( - stream: activeAccountSnapshot.hasData + subject: activeAccountSnapshot.hasData ? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities : null, builder: (final context, final capabilitiesSnapshot) { diff --git a/packages/neon/neon/lib/src/bloc/result.dart b/packages/neon/neon/lib/src/bloc/result.dart index 9840ef82..18737be2 100644 --- a/packages/neon/neon/lib/src/bloc/result.dart +++ b/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 { + /// 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 { 'Result() called without specifying the data type. Call Result() 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 { 'Result.loading() called without specifying the data type. Call Result.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 { 'Result.success() called without specifying the data type. Call Result.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 { 'Result.error() called without specifying the data type. Call Result.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 transform(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 transform(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 asLoading() => copyWith(isLoading: true); Result copyWith({ @@ -67,12 +120,26 @@ class Result { 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] 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 { @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 = Widget Function(BuildContext context, Result snapshot); + +/// Widget that builds itself based on the latest snapshot of interaction with +/// a [Stream]. +/// +/// 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 extends StreamBuilderBase, Result> { + /// 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>? 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 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? initialData; + + @override + Result initial() => initialData?.asLoading() ?? Result.loading(); + + @override + Result afterData(final Result current, final Result data) { + // prevent rebuild when only the cache state changes + if (current == data) { + return current; + } + + return data; + } + + @override + Result afterError(final Result 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 currentSummary) => builder(context, currentSummary); +} diff --git a/packages/neon/neon/lib/src/bloc/result_builder.dart b/packages/neon/neon/lib/src/bloc/result_builder.dart deleted file mode 100644 index 2d7f4cf8..00000000 --- a/packages/neon/neon/lib/src/bloc/result_builder.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:neon/src/bloc/result.dart'; -import 'package:rxdart/rxdart.dart'; - -typedef ResultWidgetBuilder = Widget Function(BuildContext context, Result snapshot); - -class ResultBuilder extends StreamBuilderBase, Result> { - const ResultBuilder({ - required this.builder, - this.initialData, - super.stream, - super.key, - }); - - ResultBuilder.behaviorSubject({ - required this.builder, - BehaviorSubject>? super.stream, - super.key, - }) : initialData = stream?.valueOrNull; - - final ResultWidgetBuilder builder; - final Result? initialData; - - @override - Result initial() => initialData?.asLoading() ?? Result.loading(); - - @override - Result afterData(final Result current, final Result data) { - // prevent rebuild when only the cache state changes - if (current == data) { - return current; - } - - return data; - } - - @override - Result afterError(final Result 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 currentSummary) => builder(context, currentSummary); -} diff --git a/packages/neon/neon/lib/src/pages/account_settings.dart b/packages/neon/neon/lib/src/pages/account_settings.dart index 4db34a89..e41b507d 100644 --- a/packages/neon/neon/lib/src/pages/account_settings.dart +++ b/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.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; diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 7826c8f6..30d2e759 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/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 { return const NeonUnifiedSearchResults(); } return ResultBuilder>.behaviorSubject( - stream: _appsBloc.appImplementations, + subject: _appsBloc.appImplementations, builder: (final context, final appImplementations) { if (!appImplementations.hasData) { return const SizedBox(); diff --git a/packages/neon/neon/lib/src/pages/login_check_account.dart b/packages/neon/neon/lib/src/pages/login_check_account.dart index 951bc187..6bb9e207 100644 --- a/packages/neon/neon/lib/src/pages/login_check_account.dart +++ b/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 { 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: [ diff --git a/packages/neon/neon/lib/src/pages/login_check_server_status.dart b/packages/neon/neon/lib/src/pages/login_check_server_status.dart index e9df9cd2..c4e4a384 100644 --- a/packages/neon/neon/lib/src/pages/login_check_server_status.dart +++ b/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 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; diff --git a/packages/neon/neon/lib/src/pages/login_flow.dart b/packages/neon/neon/lib/src/pages/login_flow.dart index 95969539..327b84cc 100644 --- a/packages/neon/neon/lib/src/pages/login_flow.dart +++ b/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 { 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: [ diff --git a/packages/neon/neon/lib/src/widgets/account_tile.dart b/packages/neon/neon/lib/src/widgets/account_tile.dart index 2a13d681..20f0e185 100644 --- a/packages/neon/neon/lib/src/widgets/account_tile.dart +++ b/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.behaviorSubject( - stream: userDetailsBloc.userDetails, + subject: userDetailsBloc.userDetails, builder: (final context, final userDetails) => Row( children: [ if (userDetails.hasData) diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart index b76424c1..9482beff 100644 --- a/packages/neon/neon/lib/src/widgets/app_bar.dart +++ b/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 { @override Widget build(final BuildContext context) => ResultBuilder>.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 { @override Widget build(final BuildContext context) => ResultBuilder.behaviorSubject( - stream: _appsBloc.notificationsAppImplementation, + subject: _appsBloc.notificationsAppImplementation, builder: (final context, final notificationsAppImplementation) { if (!notificationsAppImplementation.hasData) { return const SizedBox.shrink(); diff --git a/packages/neon/neon/lib/src/widgets/drawer.dart b/packages/neon/neon/lib/src/widgets/drawer.dart index eb6b6c9a..a412a894 100644 --- a/packages/neon/neon/lib/src/widgets/drawer.dart +++ b/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.behaviorSubject( - stream: capabilitiesBloc.capabilities, + subject: capabilitiesBloc.capabilities, builder: (final context, final capabilities) { if (!capabilities.hasData) { return NeonLinearProgressIndicator( diff --git a/packages/neon/neon/lib/src/widgets/unified_search_results.dart b/packages/neon/neon/lib/src/widgets/unified_search_results.dart index 1f7d1cef..473d8553 100644 --- a/packages/neon/neon/lib/src/widgets/unified_search_results.dart +++ b/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(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(); diff --git a/packages/neon/neon/lib/src/widgets/user_avatar.dart b/packages/neon/neon/lib/src/widgets/user_avatar.dart index c9877480..4f9ae7ef 100644 --- a/packages/neon/neon/lib/src/widgets/user_avatar.dart +++ b/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'; diff --git a/packages/neon/neon_dashboard/lib/src/pages/main.dart b/packages/neon/neon_dashboard/lib/src/pages/main.dart index 1cc456c1..d06b21e2 100644 --- a/packages/neon/neon_dashboard/lib/src/pages/main.dart +++ b/packages/neon/neon_dashboard/lib/src/pages/main.dart @@ -17,7 +17,7 @@ class DashboardMainPage extends StatelessWidget { final bloc = NeonProvider.of(context); return ResultBuilder.behaviorSubject( - stream: bloc.widgets, + subject: bloc.widgets, builder: (final context, final snapshot) { Widget? child; if (snapshot.hasData) { diff --git a/packages/neon/neon_files/lib/widgets/browser_view.dart b/packages/neon/neon_files/lib/widgets/browser_view.dart index 6ca79a8a..98d3ed90 100644 --- a/packages/neon/neon_files/lib/widgets/browser_view.dart +++ b/packages/neon/neon_files/lib/widgets/browser_view.dart @@ -42,7 +42,7 @@ class _FilesBrowserViewState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: widget.bloc.files, + subject: widget.bloc.files, builder: (final context, final filesSnapshot) => StreamBuilder>( stream: widget.bloc.path, builder: (final context, final pathSnapshot) => StreamBuilder>( diff --git a/packages/neon/neon_news/lib/dialogs/add_feed.dart b/packages/neon/neon_news/lib/dialogs/add_feed.dart index 8a5a5493..4e99e8e1 100644 --- a/packages/neon/neon_news/lib/dialogs/add_feed.dart +++ b/packages/neon/neon_news/lib/dialogs/add_feed.dart @@ -50,7 +50,7 @@ class _NewsAddFeedDialogState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: widget.bloc.folders, + subject: widget.bloc.folders, builder: (final context, final folders) => NeonDialog( title: Text(NewsLocalizations.of(context).feedAdd), children: [ diff --git a/packages/neon/neon_news/lib/widgets/articles_view.dart b/packages/neon/neon_news/lib/widgets/articles_view.dart index 2132dc2b..1a66b5e7 100644 --- a/packages/neon/neon_news/lib/widgets/articles_view.dart +++ b/packages/neon/neon_news/lib/widgets/articles_view.dart @@ -26,9 +26,9 @@ class _NewsArticlesViewState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: widget.newsBloc.feeds, + subject: widget.newsBloc.feeds, builder: (final context, final feeds) => ResultBuilder>.behaviorSubject( - stream: widget.bloc.articles, + subject: widget.bloc.articles, builder: (final context, final articles) => SortBoxBuilder( sortBox: articlesSortBox, sortProperty: widget.newsBloc.options.articlesSortPropertyOption, diff --git a/packages/neon/neon_news/lib/widgets/feeds_view.dart b/packages/neon/neon_news/lib/widgets/feeds_view.dart index c7deb7df..f36b6672 100644 --- a/packages/neon/neon_news/lib/widgets/feeds_view.dart +++ b/packages/neon/neon_news/lib/widgets/feeds_view.dart @@ -12,9 +12,9 @@ class NewsFeedsView extends StatelessWidget { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: bloc.folders, + subject: bloc.folders, builder: (final context, final folders) => ResultBuilder>.behaviorSubject( - stream: bloc.feeds, + subject: bloc.feeds, builder: (final context, final feeds) => SortBoxBuilder( sortBox: feedsSortBox, sortProperty: bloc.options.feedsSortPropertyOption, diff --git a/packages/neon/neon_news/lib/widgets/folders_view.dart b/packages/neon/neon_news/lib/widgets/folders_view.dart index 84113688..1e7a1a92 100644 --- a/packages/neon/neon_news/lib/widgets/folders_view.dart +++ b/packages/neon/neon_news/lib/widgets/folders_view.dart @@ -10,9 +10,9 @@ class NewsFoldersView extends StatelessWidget { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: bloc.folders, + subject: bloc.folders, builder: (final context, final folders) => ResultBuilder>.behaviorSubject( - stream: bloc.feeds, + subject: bloc.feeds, builder: (final context, final feeds) => SortBoxBuilder( sortBox: foldersSortBox, sortProperty: bloc.options.foldersSortPropertyOption, diff --git a/packages/neon/neon_notes/lib/dialogs/create_note.dart b/packages/neon/neon_notes/lib/dialogs/create_note.dart index b0d1dd76..ea521f22 100644 --- a/packages/neon/neon_notes/lib/dialogs/create_note.dart +++ b/packages/neon/neon_notes/lib/dialogs/create_note.dart @@ -33,7 +33,7 @@ class _NotesCreateNoteDialogState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: widget.bloc.notesList, + subject: widget.bloc.notesList, builder: (final context, final notes) => NeonDialog( title: Text(NotesLocalizations.of(context).noteCreate), children: [ diff --git a/packages/neon/neon_notes/lib/dialogs/select_category.dart b/packages/neon/neon_notes/lib/dialogs/select_category.dart index 4c87de63..04b4b275 100644 --- a/packages/neon/neon_notes/lib/dialogs/select_category.dart +++ b/packages/neon/neon_notes/lib/dialogs/select_category.dart @@ -27,7 +27,7 @@ class _NotesSelectCategoryDialogState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: widget.bloc.notesList, + subject: widget.bloc.notesList, builder: (final context, final notes) => NeonDialog( title: Text(NotesLocalizations.of(context).category), children: [ diff --git a/packages/neon/neon_notes/lib/widgets/categories_view.dart b/packages/neon/neon_notes/lib/widgets/categories_view.dart index e3109b01..5bd2d591 100644 --- a/packages/neon/neon_notes/lib/widgets/categories_view.dart +++ b/packages/neon/neon_notes/lib/widgets/categories_view.dart @@ -10,7 +10,7 @@ class NotesCategoriesView extends StatelessWidget { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: bloc.notesList, + subject: bloc.notesList, builder: (final context, final notes) => SortBoxBuilder( sortBox: categoriesSortBox, sortProperty: bloc.options.categoriesSortPropertyOption, diff --git a/packages/neon/neon_notes/lib/widgets/notes_view.dart b/packages/neon/neon_notes/lib/widgets/notes_view.dart index 35e488f2..f73cd8ef 100644 --- a/packages/neon/neon_notes/lib/widgets/notes_view.dart +++ b/packages/neon/neon_notes/lib/widgets/notes_view.dart @@ -12,7 +12,7 @@ class NotesView extends StatelessWidget { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: bloc.notesList, + subject: bloc.notesList, builder: (final context, final notesList) => SortBoxBuilder( sortBox: notesSortBox, presort: const { diff --git a/packages/neon/neon_notifications/lib/pages/main.dart b/packages/neon/neon_notifications/lib/pages/main.dart index 21c6ee5f..7783f2d3 100644 --- a/packages/neon/neon_notifications/lib/pages/main.dart +++ b/packages/neon/neon_notifications/lib/pages/main.dart @@ -25,7 +25,7 @@ class _NotificationsMainPageState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - stream: bloc.notificationsList, + subject: bloc.notificationsList, builder: (final context, final notifications) => Scaffold( resizeToAvoidBottomInset: false, floatingActionButton: StreamBuilder(