From e692a2a87e7e9cc9fd38dd7092853eb8baab7a32 Mon Sep 17 00:00:00 2001 From: Vishal Kumar Date: Tue, 26 Mar 2024 21:39:30 +0530 Subject: [PATCH 1/3] #282 Add Search Bar in Example Gallery App --- .../assets/json/home_screen.json | 28 +------ .../lib/app/home/bloc/home_bloc.dart | 75 +++++++++++++++++++ .../lib/app/home/bloc/home_event.dart | 17 +++++ .../lib/app/home/bloc/home_state.dart | 16 ++++ .../lib/app/home/home_screen.dart | 59 ++++++++++++++- 5 files changed, 168 insertions(+), 27 deletions(-) create mode 100644 examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart create mode 100644 examples/mirai_gallery/lib/app/home/bloc/home_event.dart create mode 100644 examples/mirai_gallery/lib/app/home/bloc/home_state.dart diff --git a/examples/mirai_gallery/assets/json/home_screen.json b/examples/mirai_gallery/assets/json/home_screen.json index f8be6fd7..e1d04e0a 100644 --- a/examples/mirai_gallery/assets/json/home_screen.json +++ b/examples/mirai_gallery/assets/json/home_screen.json @@ -1,26 +1,4 @@ -{ - "type": "scaffold", - "appBar": { - "type": "appBar", - "title": { - "type": "text", - "data": "Mirai Gallery" - }, - "elevation": 0 - }, - "body": { - "type": "padding", - "padding": { - "bottom": 24 - }, - "child": { - "type": "listView", - "shrinkWrap": true, - "separator": { - "type": "container", - "height": 10 - }, - "children": [ + [ { "type": "listTile", "leading": { @@ -1215,6 +1193,4 @@ } } ] - } - } -} \ No newline at end of file + \ No newline at end of file diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart b/examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart new file mode 100644 index 00000000..b988fe9f --- /dev/null +++ b/examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart @@ -0,0 +1,75 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'home_event.dart'; +part 'home_state.dart'; + +class HomeBloc extends Bloc { + HomeBloc() : super(const HomeLoading()) { + on<_Initialize>(_onInitializeEvent); + on(_onUserInputEvent); + controller = TextEditingController() + ..addListener(_textEditingControllerListener); + add(const _Initialize()); + } + + late final TextEditingController controller; + + final List> _allItems = []; + + void _onInitializeEvent(_Initialize event, Emitter emit) async { + final items = await _loadAndParseJsonFromAsset(); + if (items.isNotEmpty) { + _allItems.addAll(items); + } + emit(HomeLoaded(items: items)); + } + + void _onUserInputEvent(UserInputEvent event, Emitter emit) { + if (event.keyword.isNotEmpty) { + List> matchedItems = []; + + final String keyword = event.keyword.toLowerCase(); + for (final Map item in _allItems) { + var text = item["title"]?["data"]; + if (text != null && text is String) { + if (text.toLowerCase().contains(keyword)) { + matchedItems.add(item); + } + } + } + emit(HomeLoaded(items: matchedItems)); + } else { + emit(HomeLoaded(items: _allItems)); + } + } + + Future>> _loadAndParseJsonFromAsset() async { + try { + final String json = + await rootBundle.loadString("assets/json/home_screen.json"); + final List decodedJson = jsonDecode(json); + final List> castedJson = + decodedJson.cast>(); + return castedJson; + } catch (_) { + return const []; + } + } + + // Add `UserInputEvent` with the latest keyword + void _textEditingControllerListener() { + add(UserInputEvent(keyword: controller.text)); + } + + @override + Future close() { + controller + ..removeListener(_textEditingControllerListener) + ..dispose(); + return super.close(); + } +} diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_event.dart b/examples/mirai_gallery/lib/app/home/bloc/home_event.dart new file mode 100644 index 00000000..2a561cb1 --- /dev/null +++ b/examples/mirai_gallery/lib/app/home/bloc/home_event.dart @@ -0,0 +1,17 @@ +part of 'home_bloc.dart'; + +@immutable +sealed class HomeEvent { + const HomeEvent(); +} + +// Event used to perform the initializations of the HomeBloc +class _Initialize extends HomeEvent { + const _Initialize(); +} + +class UserInputEvent extends HomeEvent { + final String keyword; + + const UserInputEvent({required this.keyword}); +} diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_state.dart b/examples/mirai_gallery/lib/app/home/bloc/home_state.dart new file mode 100644 index 00000000..9b4b4978 --- /dev/null +++ b/examples/mirai_gallery/lib/app/home/bloc/home_state.dart @@ -0,0 +1,16 @@ +part of 'home_bloc.dart'; + +@immutable +sealed class HomeState { + final List> items; + + const HomeState({required this.items}); +} + +class HomeLoading extends HomeState { + const HomeLoading({super.items = const []}); +} + +class HomeLoaded extends HomeState { + const HomeLoaded({required super.items}); +} diff --git a/examples/mirai_gallery/lib/app/home/home_screen.dart b/examples/mirai_gallery/lib/app/home/home_screen.dart index 48f6206d..7670aebf 100644 --- a/examples/mirai_gallery/lib/app/home/home_screen.dart +++ b/examples/mirai_gallery/lib/app/home/home_screen.dart @@ -1,11 +1,68 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:mirai/mirai.dart'; +import 'bloc/home_bloc.dart'; + class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) { - return Mirai.fromAssets('assets/json/home_screen.json') ?? const SizedBox(); + return BlocProvider( + create: (context) => HomeBloc(), + child: Builder( + builder: (context) { + return Scaffold( + appBar: AppBar( + title: const Text("Mirai Gallery"), + ), + body: Column( + children: [ + TextField( + controller: BlocProvider.of(context).controller, + decoration: InputDecoration( + hintText: "Search", + prefixIcon: const Icon(Icons.search), + suffixIcon: IconButton( + onPressed: + BlocProvider.of(context).controller.clear, + icon: const Icon(Icons.clear), + ), + ), + ), + const SizedBox(height: 10.0), + Expanded( + child: BlocBuilder( + builder: (context, state) { + if (state is HomeLoaded) { + return Mirai.fromJson( + { + "type": "listView", + "shrinkWrap": false, + "separator": { + "type": "container", + "height": 10 + }, + "children": state.items, + }, + context, + ) ?? + const SizedBox(); + } + if (state is HomeLoading) { + return const CircularProgressIndicator(); + } + return const SizedBox(); + }, + ), + ), + SizedBox(height: 24.0 + MediaQuery.of(context).padding.bottom) + ], + ), + ); + }, + ), + ); } } From f91ed2de2e551f2355ac058c8fe3648fb6a68c74 Mon Sep 17 00:00:00 2001 From: Vishal Kumar Date: Mon, 1 Apr 2024 23:40:20 +0530 Subject: [PATCH 2/3] Add SizedBox inside ListView --- examples/mirai_gallery/assets/json/home_screen.json | 4 ++++ examples/mirai_gallery/lib/app/home/home_screen.dart | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/mirai_gallery/assets/json/home_screen.json b/examples/mirai_gallery/assets/json/home_screen.json index 1f2efae5..5aa7d780 100644 --- a/examples/mirai_gallery/assets/json/home_screen.json +++ b/examples/mirai_gallery/assets/json/home_screen.json @@ -1222,6 +1222,10 @@ "assetPath": "assets/json/filled_button_example.json" } } + }, + { + "type": "sizedBox", + "height": 24.0 } ] \ No newline at end of file diff --git a/examples/mirai_gallery/lib/app/home/home_screen.dart b/examples/mirai_gallery/lib/app/home/home_screen.dart index 7670aebf..1f1e5e7a 100644 --- a/examples/mirai_gallery/lib/app/home/home_screen.dart +++ b/examples/mirai_gallery/lib/app/home/home_screen.dart @@ -57,7 +57,6 @@ class HomeScreen extends StatelessWidget { }, ), ), - SizedBox(height: 24.0 + MediaQuery.of(context).padding.bottom) ], ), ); From cf9bf3cb5333c98ee20435db8a1252103e5dade9 Mon Sep 17 00:00:00 2001 From: Vishal Kumar Date: Mon, 1 Apr 2024 23:48:15 +0530 Subject: [PATCH 3/3] Replace BLoC with Cubit --- .../lib/app/home/bloc/home_event.dart | 17 ----------------- .../home_bloc.dart => cubit/home_cubit.dart} | 19 ++++++++----------- .../app/home/{bloc => cubit}/home_state.dart | 2 +- .../lib/app/home/home_screen.dart | 2 +- 4 files changed, 10 insertions(+), 30 deletions(-) delete mode 100644 examples/mirai_gallery/lib/app/home/bloc/home_event.dart rename examples/mirai_gallery/lib/app/home/{bloc/home_bloc.dart => cubit/home_cubit.dart} (75%) rename examples/mirai_gallery/lib/app/home/{bloc => cubit}/home_state.dart (91%) diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_event.dart b/examples/mirai_gallery/lib/app/home/bloc/home_event.dart deleted file mode 100644 index 2a561cb1..00000000 --- a/examples/mirai_gallery/lib/app/home/bloc/home_event.dart +++ /dev/null @@ -1,17 +0,0 @@ -part of 'home_bloc.dart'; - -@immutable -sealed class HomeEvent { - const HomeEvent(); -} - -// Event used to perform the initializations of the HomeBloc -class _Initialize extends HomeEvent { - const _Initialize(); -} - -class UserInputEvent extends HomeEvent { - final String keyword; - - const UserInputEvent({required this.keyword}); -} diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart b/examples/mirai_gallery/lib/app/home/cubit/home_cubit.dart similarity index 75% rename from examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart rename to examples/mirai_gallery/lib/app/home/cubit/home_cubit.dart index b988fe9f..3e901c2e 100644 --- a/examples/mirai_gallery/lib/app/home/bloc/home_bloc.dart +++ b/examples/mirai_gallery/lib/app/home/cubit/home_cubit.dart @@ -4,23 +4,20 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -part 'home_event.dart'; part 'home_state.dart'; -class HomeBloc extends Bloc { +class HomeBloc extends Cubit { HomeBloc() : super(const HomeLoading()) { - on<_Initialize>(_onInitializeEvent); - on(_onUserInputEvent); controller = TextEditingController() ..addListener(_textEditingControllerListener); - add(const _Initialize()); + _initialize(); } late final TextEditingController controller; final List> _allItems = []; - void _onInitializeEvent(_Initialize event, Emitter emit) async { + void _initialize() async { final items = await _loadAndParseJsonFromAsset(); if (items.isNotEmpty) { _allItems.addAll(items); @@ -28,11 +25,11 @@ class HomeBloc extends Bloc { emit(HomeLoaded(items: items)); } - void _onUserInputEvent(UserInputEvent event, Emitter emit) { - if (event.keyword.isNotEmpty) { + void _onUserInput({required String keyword}) { + if (keyword.isNotEmpty) { List> matchedItems = []; - final String keyword = event.keyword.toLowerCase(); + keyword = keyword.toLowerCase(); for (final Map item in _allItems) { var text = item["title"]?["data"]; if (text != null && text is String) { @@ -60,9 +57,9 @@ class HomeBloc extends Bloc { } } - // Add `UserInputEvent` with the latest keyword + // Listen to text editing controller for the latest keyword void _textEditingControllerListener() { - add(UserInputEvent(keyword: controller.text)); + _onUserInput(keyword: controller.text); } @override diff --git a/examples/mirai_gallery/lib/app/home/bloc/home_state.dart b/examples/mirai_gallery/lib/app/home/cubit/home_state.dart similarity index 91% rename from examples/mirai_gallery/lib/app/home/bloc/home_state.dart rename to examples/mirai_gallery/lib/app/home/cubit/home_state.dart index 9b4b4978..a0cfc1e0 100644 --- a/examples/mirai_gallery/lib/app/home/bloc/home_state.dart +++ b/examples/mirai_gallery/lib/app/home/cubit/home_state.dart @@ -1,4 +1,4 @@ -part of 'home_bloc.dart'; +part of 'home_cubit.dart'; @immutable sealed class HomeState { diff --git a/examples/mirai_gallery/lib/app/home/home_screen.dart b/examples/mirai_gallery/lib/app/home/home_screen.dart index 1f1e5e7a..98bd3396 100644 --- a/examples/mirai_gallery/lib/app/home/home_screen.dart +++ b/examples/mirai_gallery/lib/app/home/home_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:mirai/mirai.dart'; -import 'bloc/home_bloc.dart'; +import 'cubit/home_cubit.dart'; class HomeScreen extends StatelessWidget { const HomeScreen({super.key});