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
198 lines
6.5 KiB
Dart
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,
|
|
));
|
|
}
|
|
}
|
|
}
|