diff --git a/lib/features/main/routes.dart b/lib/features/main/routes.dart index 84d30e09..09725461 100644 --- a/lib/features/main/routes.dart +++ b/lib/features/main/routes.dart @@ -2,11 +2,8 @@ library main_routes; import 'package:bond/features/main/presentation/main_page.dart'; import 'package:bond/features/more/presentation/more_page.dart'; -import 'package:bond_core/bond_core.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import '../post/presentations/cubit/post_cubit.dart'; import '../post/presentations/posts_page.dart'; final mainRoutes = [ @@ -19,14 +16,7 @@ final mainRoutes = [ routes: [ GoRoute( path: '/home', - builder: (context, state) => MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => sl()..loadAllPosts(), - ), - ], - child: const PostsPage(), - ), + builder: (context, state) => const PostsPage(), ), ], ), diff --git a/lib/features/post/post_service_provider.dart b/lib/features/post/post_service_provider.dart index b4673b75..b8358543 100644 --- a/lib/features/post/post_service_provider.dart +++ b/lib/features/post/post_service_provider.dart @@ -3,13 +3,11 @@ import 'package:get_it/get_it.dart'; import 'data/api.dart'; import 'data/models/post.dart'; -import 'presentations/cubit/post_cubit.dart'; class PostServiceProvider extends ServiceProvider with ResponseDecoding { @override Future register(GetIt it) async { it.registerFactory(() => PostsApi(it())); - it.registerFactory(() => PostCubit(it())); } @override diff --git a/lib/features/post/presentations/cubit/post_cubit.dart b/lib/features/post/presentations/cubit/post_cubit.dart deleted file mode 100644 index 0e9c62b1..00000000 --- a/lib/features/post/presentations/cubit/post_cubit.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:bond/features/post/data/api.dart'; -import 'package:bond/features/post/data/models/post.dart'; -import 'package:bond_network/bond_network.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/cupertino.dart'; - -part 'post_state.dart'; - -class PostCubit extends Cubit> { - PostCubit(this.api) : super(ListState.initial()) { - scrollController.addListener(_scrollControllerListener); - } - - final PostsApi api; - - final ScrollController scrollController = ScrollController(); - - Future loadAllPosts() async { - emit(state.loading()); - final response = await api.posts(); - emit(state.success(response)); - } - - @override - Future close() async { - scrollController.dispose(); - scrollController.removeListener(_scrollControllerListener); - super.close(); - } - - void _scrollControllerListener() { - double maxScroll = scrollController.position.maxScrollExtent; - double currentScroll = scrollController.position.pixels; - double delta = 200.0; - if (maxScroll - currentScroll <= delta && - state.status != ListStatus.loading) { - loadAllPosts(); - } - } -} diff --git a/lib/features/post/presentations/posts_page.dart b/lib/features/post/presentations/posts_page.dart index c7749d63..da10c578 100644 --- a/lib/features/post/presentations/posts_page.dart +++ b/lib/features/post/presentations/posts_page.dart @@ -1,22 +1,23 @@ +import 'package:bond/features/post/presentations/providers/posts_provider.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'cubit/post_cubit.dart'; import 'views/home_app_bar.dart'; import 'views/post_item.dart'; -class PostsPage extends StatelessWidget { +class PostsPage extends ConsumerWidget { const PostsPage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - final postCubit = context.watch(); + Widget build(BuildContext context, WidgetRef ref) { + final postsState = ref.watch(postsProvider); + final postsController = ref.read(postsProvider.notifier); return Scaffold( appBar: const HomeAppBar(), - body: postCubit.state.when( - initial: () => const Center(child: CircularProgressIndicator()), - success: (posts, loading) => SingleChildScrollView( - controller: postCubit.scrollController, + body: postsState.when( + loading: () => const Center(child: CircularProgressIndicator()), + data: (posts) => SingleChildScrollView( + controller: postsController.scrollController, child: Column( children: [ GridView.count( @@ -27,16 +28,16 @@ class PostsPage extends StatelessWidget { childAspectRatio: 0.6, mainAxisSpacing: 6.0, crossAxisSpacing: 6.0, - children: posts.map((post) { + children: posts.data.data.map((post) { return PostItem(post: post); }).toList(), ), - if (loading) const CircularProgressIndicator.adaptive(), + if (posts.isLoading) const CircularProgressIndicator.adaptive(), ], ), ), - failed: (error) => Center( - child: Text(error), + error: (error, _) => Center( + child: Text(error.toString()), ), ), ); diff --git a/lib/features/post/presentations/cubit/post_state.dart b/lib/features/post/presentations/providers/list_state.dart similarity index 82% rename from lib/features/post/presentations/cubit/post_state.dart rename to lib/features/post/presentations/providers/list_state.dart index 55370a7a..60b2f9e6 100644 --- a/lib/features/post/presentations/cubit/post_state.dart +++ b/lib/features/post/presentations/providers/list_state.dart @@ -1,4 +1,7 @@ -part of 'post_cubit.dart'; +// extract to bond network package +import 'package:bond_network/bond_network.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/widgets.dart'; enum ListStatus { initial, loading, success, failed } @@ -11,12 +14,20 @@ class ListState extends Equatable { final String? error; + bool get isLoading => status == ListStatus.loading; + factory ListState.initial() => ListState( ListResponse(data: List.empty(growable: true)), ListStatus.initial, null, ); + factory ListState.data(ListResponse data) => ListState( + data, + ListStatus.success, + null, + ); + ListState loading() => copyWith(status: ListStatus.loading); ListState success(ListResponse newData) => copyWith( diff --git a/lib/features/post/presentations/providers/posts_provider.dart b/lib/features/post/presentations/providers/posts_provider.dart new file mode 100644 index 00000000..0c3cefb6 --- /dev/null +++ b/lib/features/post/presentations/providers/posts_provider.dart @@ -0,0 +1,55 @@ +import 'package:bond/features/post/data/api.dart'; +import 'package:bond/features/post/data/models/post.dart'; +import 'package:bond_core/bond_core.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'list_state.dart'; + +final postsProvider = + AsyncNotifierProvider.autoDispose>( + () { + final scrollController = ScrollController(); + + return PostController(sl(), scrollController); + }, +); + +class PostController extends AutoDisposeAsyncNotifier> { + PostController(this._api, this.scrollController) : super() { + scrollController.addListener(_scrollControllerListener); + ref.onDispose( + () { + scrollController.dispose(); + scrollController.removeListener(_scrollControllerListener); + }, + ); + } + + final PostsApi _api; + final ScrollController scrollController; + + @override + Future> build() async { + final response = await _api.posts(); + return ListState.data(response); + } + + void loadMore() async { + state = AsyncData(state.requireValue.loading()); + state = await AsyncValue.guard(() async { + final response = await _api.posts(); + return state.requireValue.success(response); + }); + } + + void _scrollControllerListener() { + final maxScroll = scrollController.position.maxScrollExtent; + final currentScroll = scrollController.position.pixels; + const delta = 200.0; + if (maxScroll - currentScroll <= delta && + state.value?.status != ListStatus.loading) { + loadMore(); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index 49ce4f1e..64620557 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,14 +49,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - bloc: - dependency: "direct main" - description: - name: bloc - sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" - url: "https://pub.dev" - source: hosted - version: "8.1.4" bond_app_analytics: dependency: "direct main" description: @@ -478,14 +470,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_bloc: - dependency: "direct main" - description: - name: flutter_bloc - sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 - url: "https://pub.dev" - source: hosted - version: "8.1.5" flutter_launcher_icons: dependency: "direct dev" description: @@ -829,14 +813,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" open_store: dependency: "direct main" description: @@ -997,14 +973,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" - provider: - dependency: transitive - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" pub_semver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d6057d32..f03acfcd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,8 +41,6 @@ dependencies: google_fonts: ^5.0.0 # state management - bloc: ^8.0.2 - flutter_bloc: ^8.0.1 flutter_riverpod: ^2.5.1 equatable: ^2.0.5