flutterdartwidgetoverflow

I am unable to fix the overflow issue in my app that I am practicing with flutter from a course


The problem is that whenever I open the model bottom sheet and when the keyboard appears on pressing a text field the chart widget behind the ModalBottomSheet overflows and this only happens when I switch to landscape mode.

link to a clip of the problem https://s1.ezgif.com/tmp/ezgif-1680ad1de76705.gif

Below is the code for BottomSheet Widget and The Widget that wraps everything:

BottomSheet.dart

import 'package:expense_tracker/model/expense.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class ExpenseAdd extends StatefulWidget {
 const ExpenseAdd({required this.addExpense,required this.removeExpense ,super.key});
 final void Function(Expense expense) addExpense;
 final void Function(Expense expense) removeExpense;

 

 @override
 State<ExpenseAdd> createState() => _ExpenseAddState();
}

class _ExpenseAddState extends State<ExpenseAdd> {
 final TextEditingController _titleController = TextEditingController();
 final TextEditingController _amountController = TextEditingController();
 Category category = Category.leisure;
 DateTime? daTe;

 void _datePickerOverlay() {
   final date = DateTime.now();
   final firstDate = DateTime(date.year - 1);
   

   showDatePicker(
     context: context,
     firstDate: firstDate,
     lastDate: date,
     initialDate: date,
   ).then((selectedDate) {
     setState(() {
       daTe = selectedDate;
     });
   });
 }

 void _checkSubmittion() {
   final String title = _titleController.text;
   final String amount = _amountController.text;

   if (title.trim().isEmpty || amount.trim().isEmpty || daTe == null) {
     showDialog(
       context: context,
       builder: (ctx) {
         return AlertDialog(
           title: Text('Alert'),
           content: Text(
             'Make sure you have entered correct input and left no required fields empty.',
           ),
         );
       },
     );
     return;
   }
   widget.addExpense(Expense(title: title, amount: double.parse(amount), date: daTe!, category: category,),);
   Navigator.pop(context);
   return;

 }

 @override
 void dispose() {
   _titleController.dispose();
   _amountController.dispose();
   super.dispose();
 }

 @override
 Widget build(context) {
 
   return 
      SizedBox(
       height: double.infinity,
        child: SingleChildScrollView(
          child: Padding(
             padding: EdgeInsets.fromLTRB(16, 48, 16, 0),
             child: Column(
               children: [
                 const SizedBox(height: 20),
                 Text('Add New Expense'),
                 const SizedBox(height: 20),
                 TextField(
                   
                   controller: _titleController,
                   decoration: InputDecoration(
                      prefixIcon: Icon(Icons.edit),
                     label: Text('Title'),
                     border: OutlineInputBorder(),
                   ),
                 ),
                 const SizedBox(height: 20),
                Row(
                     children: [
                       Expanded(
                         child: TextField(
                           keyboardType: TextInputType.number,
                           controller: _amountController,
                           decoration: InputDecoration(
           
                             prefixIcon: Icon(Icons.monetization_on_sharp),
                             label: Text('Amount'),
                             border: OutlineInputBorder(
                               
                             ),
                           ),
                         ),
                       ),
                       const SizedBox(width: 20),
                       Flexible(
                         child: Row(
                           children: [
                             Spacer(),
                             Flexible(
                               child: Text(
                                 (daTe == null
                                     ? 'No date selected'
                                     : formatter.format(daTe!)),
                               ),
                             ),
                             IconButton(
                               onPressed: () {
                                 _datePickerOverlay();
                               },
                               icon: Icon(Icons.calendar_month),
                             ),
                           ],
                         ),
                       ),
                     ],
                   ),
                 
                 const SizedBox(height: 20),
                Row(
                     children: [
                       const SizedBox(width: 10),
                       DropdownButton(
                         value: category,
                         items:
                             Category.values.map((category) {
                               return DropdownMenuItem(
                                 value: category,
                                 child: Text(category.name.toString()),
                               );
                             }).toList(),
                         onChanged: (value) {
                           setState(() {
                             category = value!;
                           });
                         },
                       ),
                       Flexible(
                         child: Row(
                           children: [
                             Spacer(),
                             TextButton(
                               onPressed: () {
                                 Navigator.pop(context);
                               },
                               child: Text('Cancel'),
                             ),
                             const SizedBox(width: 10),
                             FilledButton(onPressed: () {
                               _checkSubmittion();
                             }, child: Text('Save')),
                           ],
                         ),
                       ),
                     ],
                   ),
                 
               ],
             ),
           
                
              ),
        ),
      );
 }
}

expense_tracker.dart

import 'package:expense_tracker/model/expense.dart';
import 'package:expense_tracker/widget/chart.dart';
import 'package:expense_tracker/widget/expense_add.dart';
import 'package:expense_tracker/widget/list_of_expense.dart';
import 'package:flutter/material.dart';

class ExpenseTracker extends StatefulWidget {
  const ExpenseTracker({super.key});

  @override
  State<ExpenseTracker> createState() => _ExpenseTrackerState();
}

class _ExpenseTrackerState extends State<ExpenseTracker> {
  final List<Expense> expenses = [
   
  ];

  void _addExpenseOverlay() {
    showModalBottomSheet(
      useSafeArea: true,
      isScrollControlled: true,
      context: context,
      builder: (ctx) {
        return ExpenseAdd(
          addExpense: _addExpense,
          removeExpense: _removeExpense,
        );
      },
    );
  }

  void _addExpense(Expense expense) {
    setState(() {
      expenses.add(expense);
    });
  }

  void _removeExpense(Expense expense) {
    setState(() {
      expenses.remove(expense);
    });
  }

  void _undoDelete(Expense expense, index) {
    setState(() {
      expenses.insert(index, expense);
    });
  }

  @override
  Widget build(context) {
    final width  = MediaQuery.of(context).size.width;
    return Scaffold(
      resizeToAvoidBottomInset: true,
      appBar: AppBar(
        title: Text('XP Tracker'),
        actions: [
          IconButton(
            onPressed: () {
              _addExpenseOverlay();
            },
            icon: Icon(Icons.add),
          ),
        ],
      ),
      body: Center(
        child: width < 600 ? Column(
          children: [
            Chart(expenses: expenses),
            (expenses.isEmpty
                ? Center(child: Text('No Expense found.'))
                : Expanded(
                  child: ListOfExpense(
                    expenses: expenses,
                    removeExpense: _removeExpense,
                    undoDelete: _undoDelete,
                  ),
                )),
          ],
        ) : Row(
          children: [
            Expanded(child: Chart(expenses: expenses)),
            (expenses.isEmpty
                ? Center(child: Text('No Expense found.'))
                : Expanded(
                  child: ListOfExpense(
                    expenses: expenses,
                    removeExpense: _removeExpense,
                    undoDelete: _undoDelete,
                  ),
                )),
          ],
        ),
      ),
    );
  }
}


Solution

  • When the keyboard appears, the Row that contains your Chart does not have a sufficient height, and a layout error occurs in your Chart. We don't know what the chart looks like, but one of its children requires more height than available with the keyboard present in landscape mode.

    In such a case, a possible solution can be to wrap your Scaffold body in a SingleChildScrollView, so that when the space becomes too small, it will automatically scroll. But again, it really depends on what's actually going on in your Chart widget.