Fix vertical overflow by removing nested Scaffold architecture

- Remove nested Scaffold from ScoringPage (now uses LayoutBuilder)
- Use proper constraint-based height calculation instead of MediaQuery
- Maintain score preservation across navigation without nested scaffolds
- Fix viewport architecture to work within MainNavigationPage body
- Minor keypad layout adjustment (77%/23% flex ratio)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2025-08-24 13:28:43 +09:00
parent fd5a034ed4
commit 8f7d870985
16 changed files with 1289 additions and 86 deletions
@@ -123,7 +123,7 @@ class _MainNavigationPageState extends State<MainNavigationPage> {
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'Professional archery scoring with sophisticated end interaction system.',
'REPLACE ALL THESE TEXTS WITH LOCALISATION MARKERS!!',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
+64 -64
View File
@@ -383,78 +383,78 @@ class _ScoringPageState extends State<ScoringPage> 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
final keypadHeight = (availableHeight * 0.30 * _keypadAnimation.value).clamp(0.0, availableHeight * 0.30);
final upperViewportHeight = (availableHeight - keypadHeight).clamp(100.0, availableHeight);
return Scaffold(
body: GestureDetector(
onTap: _onBackgroundTapped,
behavior: HitTestBehavior.opaque,
child: Column(
children: [
// Upper viewport - Settings and Scoring grid (scrollable)
SizedBox(
height: upperViewportHeight,
child: Column(
children: [
// Settings toggles
Padding(
padding: const EdgeInsets.all(16.0),
child: ToggleSwitches(
arrowsPerEnd: arrowsPerEnd,
onArrowsPerEndChanged: _onArrowsPerEndChanged,
),
),
// Scoring grid (scrollable within upper viewport)
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ScoringGrid(
scores: scores,
// Since we're inside MainNavigationPage's Scaffold body, use LayoutBuilder to get actual constraints
return LayoutBuilder(
builder: (context, constraints) {
final availableHeight = constraints.maxHeight.clamp(200.0, double.infinity); // Minimum 200px
final keypadHeight = (availableHeight * 0.30 * _keypadAnimation.value).clamp(0.0, availableHeight * 0.30);
final upperViewportHeight = (availableHeight - keypadHeight).clamp(100.0, availableHeight);
return GestureDetector(
onTap: _onBackgroundTapped,
behavior: HitTestBehavior.opaque,
child: Column(
children: [
// Upper viewport - Settings and Scoring grid (scrollable)
SizedBox(
height: upperViewportHeight,
child: Column(
children: [
// Settings toggles
Padding(
padding: const EdgeInsets.all(16.0),
child: ToggleSwitches(
arrowsPerEnd: arrowsPerEnd,
endsPerSet: endsPerSet,
selectedRow: selectedRow,
selectedColumn: selectedColumn,
activeEndIndex: activeEndIndex,
onCellSelected: _onCellSelected,
onArrowsPerEndChanged: _onArrowsPerEndChanged,
),
),
),
],
),
),
// Lower viewport - Keypad (animated height)
if (keypadHeight > 0)
SizedBox(
height: keypadHeight,
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
border: Border(
top: BorderSide(color: Colors.grey.shade300, width: 1),
// Scoring grid (scrollable within upper viewport)
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ScoringGrid(
scores: scores,
arrowsPerEnd: arrowsPerEnd,
endsPerSet: endsPerSet,
selectedRow: selectedRow,
selectedColumn: selectedColumn,
activeEndIndex: activeEndIndex,
onCellSelected: _onCellSelected,
),
),
),
),
child: Opacity(
opacity: _keypadAnimation.value,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ScoringKeypad(
onScoreInput: _onScoreInput,
],
),
),
// Lower viewport - Keypad (animated height)
if (keypadHeight > 0)
SizedBox(
height: keypadHeight,
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade100,
border: Border(
top: BorderSide(color: Colors.grey.shade300, width: 1),
),
),
child: Opacity(
opacity: _keypadAnimation.value,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: ScoringKeypad(
onScoreInput: _onScoreInput,
),
),
),
),
),
),
],
),
),
],
),
);
},
);
},
);
@@ -32,9 +32,9 @@ class ScoringKeypad extends StatelessWidget {
// Use Flex widgets for proper percentage distribution
return Row(
children: [
// Score buttons section (75% width)
// Score buttons section (77% width)
Expanded(
flex: 75,
flex: 77,
child: Column(
children: List.generate(4, (rowIndex) {
return Expanded(
@@ -59,9 +59,9 @@ class ScoringKeypad extends StatelessWidget {
const SizedBox(width: 2),
// Functional buttons section (25% width)
// Functional buttons section (23% width)
Expanded(
flex: 25,
flex: 23,
child: Column(
children: List.generate(4, (index) {
final buttonText = functionalButtons[index];