Collapsing header UI (#173)

* Add collapsing / expanding header behavior

* Improve header behavior with scroll-direction based expansion
This commit is contained in:
itsgnex
2026-01-18 03:37:55 -08:00
committed by GitHub
parent b3de2931da
commit adaf0f655e
2 changed files with 65 additions and 72 deletions

5
.gitignore vendored
View File

@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
@@ -42,3 +44,6 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
*.jdk/
openJdk-25.jdk/
oracleJdk-25.jdk/

View File

@@ -4,6 +4,7 @@ import 'dart:io' show Platform;
// Flutter imports:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/rendering.dart'; // <-- REQUIRED
// Package imports:
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -40,25 +41,21 @@ void main() async {
MyLibraryDb dataBase = MyLibraryDb.instance;
bool isDarkMode =
await dataBase.getPreference('darkMode') == 0 ? false : true;
bool openPdfwithExternalapp = await dataBase
.getPreference('openPdfwithExternalApp')
.catchError((e) => print(e)) ==
0
? false
: true;
bool openEpubwithExternalapp = await dataBase
.getPreference('openEpubwithExternalApp')
.catchError((e) => print(e)) ==
0
? false
: true;
bool openPdfwithExternalapp =
await dataBase.getPreference('openPdfwithExternalApp') == 0
? false
: true;
bool openEpubwithExternalapp =
await dataBase.getPreference('openEpubwithExternalApp') == 0
? false
: true;
String browserUserAgent = await dataBase.getBrowserOptions('userAgent');
String browserCookie = await dataBase.getBrowserOptions('cookie');
if (Platform.isAndroid) {
//[SystemChrome] Also change colors in settings page Theme colors if any change
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
systemNavigationBarColor:
isDarkMode ? Colors.black : Colors.grey.shade200));
@@ -84,14 +81,14 @@ void main() async {
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
builder: (BuildContext context, Widget? child) {
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: const TextScaler.linear(1.0),
),
data: MediaQuery.of(context)
.copyWith(textScaler: const TextScaler.linear(1.0)),
child: child!,
);
},
@@ -117,25 +114,50 @@ class _MainScreenState extends ConsumerState<MainScreen> {
HomePage(),
SearchPage(),
MyLibraryPage(),
SettingsPage()
SettingsPage(),
];
bool _showExpandedHeader = true; // <-- ONLY new state
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final selectedIndex = ref.watch(selectedIndexProvider);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.surface,
title: const Text("Openlib"),
titleTextStyle: Theme.of(context).textTheme.displayLarge,
body: NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.reverse &&
_showExpandedHeader) {
setState(() => _showExpandedHeader = false);
} else if (notification.direction == ScrollDirection.forward &&
!_showExpandedHeader) {
setState(() => _showExpandedHeader = true);
}
return false;
},
child: Column(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: _showExpandedHeader ? kToolbarHeight : 0,
child: AppBar(
backgroundColor: Theme.of(context).colorScheme.surface,
title: const Text("Openlib"),
titleTextStyle:
Theme.of(context).textTheme.displayLarge,
),
),
Expanded(
child: _widgetOptions.elementAt(selectedIndex),
),
],
),
),
body: _widgetOptions.elementAt(selectedIndex),
bottomNavigationBar: SafeArea(
child: GNav(
backgroundColor: isDarkMode ? Colors.black : Colors.grey.shade200,
backgroundColor:
isDarkMode ? Colors.black : Colors.grey.shade200,
haptic: true,
tabBorderRadius: 50,
tabActiveBorder: Border.all(
@@ -145,55 +167,21 @@ class _MainScreenState extends ConsumerState<MainScreen> {
curve: Curves.fastLinearToSlowEaseIn,
duration: const Duration(milliseconds: 25),
gap: 5,
color: const Color.fromARGB(255, 255, 255, 255),
activeColor: const Color.fromARGB(255, 255, 255, 255),
iconSize: 19, // tab button icon size
tabBackgroundColor: Theme.of(context).colorScheme.secondary,
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 6.5),
tabs: [
GButton(
icon: Icons.trending_up,
text: 'Home',
iconColor: isDarkMode ? Colors.white : Colors.black,
textStyle: const TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 11,
),
),
GButton(
icon: Icons.search,
text: 'Search',
iconColor: isDarkMode ? Colors.white : Colors.black,
textStyle: const TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 11,
),
),
GButton(
icon: Icons.collections_bookmark,
text: 'My Library',
iconColor: isDarkMode ? Colors.white : Colors.black,
textStyle: const TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 11,
),
),
GButton(
icon: Icons.build,
text: 'Settings',
iconColor: isDarkMode ? Colors.white : Colors.black,
textStyle: const TextStyle(
fontWeight: FontWeight.w900,
color: Colors.white,
fontSize: 11,
),
),
color: Colors.white,
activeColor: Colors.white,
iconSize: 19,
tabBackgroundColor:
Theme.of(context).colorScheme.secondary,
padding:
const EdgeInsets.symmetric(horizontal: 13, vertical: 6.5),
tabs: const [
GButton(icon: Icons.trending_up, text: 'Home'),
GButton(icon: Icons.search, text: 'Search'),
GButton(icon: Icons.collections_bookmark, text: 'My Library'),
GButton(icon: Icons.build, text: 'Settings'),
],
selectedIndex: selectedIndex,
onTabChange: (index) async {
onTabChange: (index) {
ref.read(selectedIndexProvider.notifier).state = index;
},
),