olshab f0a17af845 final improvements
added internet permission in manifest
added unique app bar titles for each section
fixed card layout
fixed card details page layout
added appbar button for toggling dark mode
2024-12-17 14:31:39 +04:00

198 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_android_app/components/extensions/context_x.dart';
import 'package:flutter_android_app/components/utils/debounce.dart';
import 'package:flutter_android_app/domain/models/card.dart';
import 'package:flutter_android_app/presentation/details_page/details_page.dart';
import 'package:flutter_android_app/presentation/favourites_page/favourites_page.dart';
import 'package:flutter_android_app/presentation/home_page/bloc/bloc.dart';
import 'package:flutter_android_app/presentation/home_page/bloc/events.dart';
import 'package:flutter_android_app/presentation/home_page/bloc/state.dart';
import 'package:flutter_android_app/presentation/home_page/cards_list.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../common/svg_objects.dart';
import '../favourites_bloc/favourites_bloc.dart';
import '../favourites_bloc/favourites_events.dart';
import '../settings_page/settings_page.dart';
class MainScaffold extends StatefulWidget {
const MainScaffold({
super.key,
this.toggleDarkMode,
required this.isDarkModeSelected,
});
final void Function()? toggleDarkMode;
final bool isDarkModeSelected;
@override
State<MainScaffold> createState() => _MainScaffoldState();
}
class _MainScaffoldState extends State<MainScaffold> {
int currentPageIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: [
Text(context.locale.mainAppBarTitle),
Text(context.locale.favouritesPageAppBarTitle),
Text(context.locale.settingsPageAppBarTitle),
][currentPageIndex],
actions: [
Padding(
padding: const EdgeInsets.only(right: 12.0),
child: IconButton(
isSelected: widget.isDarkModeSelected,
onPressed: () => widget.toggleDarkMode?.call(),
icon: const Icon(Icons.wb_sunny_outlined),
selectedIcon: const Icon(Icons.brightness_2_outlined),
),
),
],
),
bottomNavigationBar: NavigationBar(
destinations: [
NavigationDestination(icon: const Icon(Icons.home), label: context.locale.navigationHome),
NavigationDestination(icon: const Icon(Icons.favorite), label: context.locale.navigationFavourites),
NavigationDestination(icon: const Icon(Icons.settings), label: context.locale.navigationSettings),
],
selectedIndex: currentPageIndex,
onDestinationSelected: (int index) => setState(() {
currentPageIndex = index;
}),
),
body: [
const HomePage(),
const FavouritesPage(),
const SettingsPage(),
][currentPageIndex],
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController searchController = TextEditingController();
@override
void initState() {
SvgObjects.init();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeBloc>().add(HomeLoadDataEvent(locale: context.locale));
context.read<FavouritesBloc>().add(const LoadFavouritesEvent());
});
super.initState();
}
@override
void dispose() {
searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: SearchBar(
controller: searchController,
onChanged: (search) {
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search, locale: context.locale)));
},
leading: const Icon(Icons.search),
trailing: [
IconButton(
icon: const Icon(Icons.close),
onPressed: () {
if (searchController.text.isNotEmpty) {
searchController.clear();
context.read<HomeBloc>().add(HomeLoadDataEvent(locale: context.locale));
}
},
),
],
hintText: context.locale.searchHint,
elevation: const WidgetStatePropertyAll(0.0),
padding: const WidgetStatePropertyAll(EdgeInsets.only(left: 18, right: 10)),
backgroundColor: WidgetStatePropertyAll(Theme.of(context).colorScheme.secondaryContainer),
),
),
CardsList(
onListRefresh: _onRefresh,
onCardLiked: _onLike,
onCardTapped: _navToDetails,
onNextPage: _onNextPage,
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.isPaginationLoading
? const Padding(
padding: EdgeInsets.all(12),
child: CircularProgressIndicator(),
)
: const SizedBox.shrink(),
),
],
)
);
}
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'$title ${isLiked ? context.locale.addedToFavourite : context.locale.removedFromFavourite}',
style: Theme.of(context).textTheme.bodyLarge
),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
duration: const Duration(seconds: 2),
));
});
}
Future<void> _onRefresh() {
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text, locale: context.locale));
return Future.value(null);
}
void _onLike(String? id, String title, bool isLiked) {
if (id != null) {
context.read<FavouritesBloc>().add(ChangeFavouriteEvent(id));
_showSnackBar(context, title, !isLiked);
}
}
void _navToDetails(CardData data) {
Navigator.push(context, MaterialPageRoute<void>(
builder: (context) => DetailsPage(data)),
);
}
void _onNextPage() {
final bloc = context.read<HomeBloc>();
if (!bloc.state.isPaginationLoading && !bloc.state.isAllPagesLoaded) {
bloc.add(HomeLoadDataEvent(
search: searchController.text,
nextPage: bloc.state.data?.nextPage,
locale: context.locale,
));
}
}
}