From c57418ea8be023fcb37520b054de1c9d78285925 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Sat, 23 Aug 2025 22:42:32 +0900 Subject: [PATCH] Add swipe-in side navigation with Main/Settings pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements professional navigation structure with Material Design drawer: - MainNavigationPage: Central navigation hub with AppBar and Drawer - SettingsPage: Placeholder settings page for future configuration options - Drawer navigation: Swipe-in side menu with Main (Scorer) and Settings options - App title updates dynamically based on current page selection - Clean architecture separating navigation from scoring logic Navigation features: - Material Design drawer with branded header showing 'Archery Bookkeeper Alpha 0.1' - Icons for visual clarity (sports for scorer, settings for configuration) - Selected state highlighting for current page - Automatic drawer close on navigation selection - Professional app description in drawer footer Updated main.dart to use MainNavigationPage as entry point Modified ScoringPage to work as embedded page without its own AppBar Maintained all sophisticated end interaction behaviors within scoring context This establishes the foundation for additional pages and features while preserving the advanced scoring functionality implemented in the previous release. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- lib/main.dart | 4 +- .../pages/main_navigation_page.dart | 99 +++++++++++++++++++ lib/presentation/pages/scoring_page.dart | 5 +- lib/presentation/pages/settings_page.dart | 52 ++++++++++ 4 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 lib/presentation/pages/main_navigation_page.dart create mode 100644 lib/presentation/pages/settings_page.dart diff --git a/lib/main.dart b/lib/main.dart index 78f034b..4909f5e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'presentation/pages/scoring_page.dart'; +import 'presentation/pages/main_navigation_page.dart'; void main() { runApp(const ArcheryBookkeeperApp()); @@ -19,7 +19,7 @@ class ArcheryBookkeeperApp extends StatelessWidget { ), useMaterial3: true, ), - home: const ScoringPage(), + home: const MainNavigationPage(), ); } } \ No newline at end of file diff --git a/lib/presentation/pages/main_navigation_page.dart b/lib/presentation/pages/main_navigation_page.dart new file mode 100644 index 0000000..dbc9405 --- /dev/null +++ b/lib/presentation/pages/main_navigation_page.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'scoring_page.dart'; +import 'settings_page.dart'; + +class MainNavigationPage extends StatefulWidget { + const MainNavigationPage({super.key}); + + @override + State createState() => _MainNavigationPageState(); +} + +class _MainNavigationPageState extends State { + int _selectedIndex = 0; + + final List _pages = [ + const ScoringPage(), + const SettingsPage(), + ]; + + final List _pageTitles = [ + 'Main (Scorer)', + 'Settings', + ]; + + void _onNavigationItemSelected(int index) { + setState(() { + _selectedIndex = index; + }); + Navigator.pop(context); // Close drawer + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(_pageTitles[_selectedIndex]), + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + ), + body: _pages[_selectedIndex], + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.inversePrimary, + ), + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + 'Archery Bookkeeper', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Text( + 'Alpha 0.1', + style: TextStyle( + color: Colors.white70, + fontSize: 16, + ), + ), + ], + ), + ), + ListTile( + leading: const Icon(Icons.sports), + title: const Text('Main (Scorer)'), + selected: _selectedIndex == 0, + onTap: () => _onNavigationItemSelected(0), + ), + ListTile( + leading: const Icon(Icons.settings), + title: const Text('Settings'), + selected: _selectedIndex == 1, + onTap: () => _onNavigationItemSelected(1), + ), + const Divider(), + const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + 'Professional archery scoring with sophisticated end interaction system.', + style: TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/presentation/pages/scoring_page.dart b/lib/presentation/pages/scoring_page.dart index 13221e3..d67eb59 100644 --- a/lib/presentation/pages/scoring_page.dart +++ b/lib/presentation/pages/scoring_page.dart @@ -204,6 +204,7 @@ class _ScoringPageState extends State with SingleTickerProviderStat animation: _keypadAnimation, builder: (context, child) { // Calculate viewport heights based on animation progress + // AppBar is in parent navigation, account for status bar and safe area final availableHeight = (MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top - kToolbarHeight).clamp(200.0, double.infinity); // Minimum 200px @@ -212,10 +213,6 @@ class _ScoringPageState extends State with SingleTickerProviderStat final upperViewportHeight = (availableHeight - keypadHeight).clamp(100.0, availableHeight); return Scaffold( - appBar: AppBar( - title: const Text('Archery Scoring'), - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - ), body: GestureDetector( onTap: _onBackgroundTapped, behavior: HitTestBehavior.opaque, diff --git a/lib/presentation/pages/settings_page.dart b/lib/presentation/pages/settings_page.dart new file mode 100644 index 0000000..1206984 --- /dev/null +++ b/lib/presentation/pages/settings_page.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +class SettingsPage extends StatelessWidget { + const SettingsPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Settings'), + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + ), + body: const Padding( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Settings', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 24), + Text( + 'This is a placeholder settings page.', + style: TextStyle( + fontSize: 16, + color: Colors.grey, + ), + ), + SizedBox(height: 16), + Text( + 'Future settings will include:', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(height: 8), + Text('• Color scheme preferences'), + Text('• Default arrow counts'), + Text('• Score input preferences'), + Text('• Export settings'), + Text('• Session management'), + ], + ), + ), + ); + } +} \ No newline at end of file