flutterflutter-listviewflutter-alertdialogflutter-dialog

Flutter ListView in AlertDialog overlaps with Dialog Title


I have a simple ListView in my alert dialog. It allows a user to make selection and once selected, the list tile color changes.

The weird issue that I am having is for the selected tiles, when I scroll, it overlaps with the title of the dialog:

enter image description here enter image description here

But for the other unselected tiles, it just goes under the title as desired.

My code is as below:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mauritius_emergency_services/core/models/service.dart';
import 'package:mauritius_emergency_services/core/providers/services.dart';
import 'package:mauritius_emergency_services/core/providers/settings.dart';

class EmergencyButtonDialog extends ConsumerWidget {
  const EmergencyButtonDialog({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final settings = ref.watch(settingsProvider);
    final uiState = ref.watch(servicesProvider).when(
          data: (services) => ServiceListView(
            services: services,
            selectedService: settings.emergencyButtonAction,
            onServiceSelected: (service) {
              ref
                  .read(settingsProvider.notifier)
                  .updateEmergencyButtonAction(service);
            },
          ),
          loading: () => const CircularProgressIndicator(),
          error: (error, stack) => const Text("Error occurred"),
        );

    return AlertDialog(
      title: const Text("Choose Emergency Action"),
      contentPadding: EdgeInsets.zero, // Remove default padding
      content: SizedBox(
        width: double.maxFinite,
        height: 300,
        child: uiState,
      ),
    );
  }
}

class ServiceListView extends StatelessWidget {
  final List<Service> services;
  final Service selectedService;
  final Function(Service) onServiceSelected;

  const ServiceListView({
    super.key,
    required this.services,
    required this.selectedService,
    required this.onServiceSelected,
  });

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: services
          .map(
            (service) => ListTile(
              selected: service.identifier == selectedService.identifier,
              selectedColor: Theme.of(context).colorScheme.onTertiary,
              selectedTileColor: Theme.of(context).colorScheme.tertiary,
              title: Text(service.name),
              subtitle: Text(service.mainContact.toString()),
              trailing: service.identifier == selectedService.identifier
                  ? Icon(
                      Icons.check_circle,
                      color: Theme.of(context).colorScheme.onTertiary,
                    )
                  : null,
              onTap: () {
                onServiceSelected(service);
              },
            ),
          )
          .toList(),
    );
  }
}

And this is how i launch my dialog:

showDialog<String>(
              context: context,
              builder: (BuildContext context) => LanguageDialog(),
            );

Anyone know why this is happening ?


Solution

  • Solution for this is to remove the title padding of AlertDialog then surrounding the Text Widget of title property of AlertDialog by Container Widget , then giving Container the same color and height as that of AlertDialog title and finally changing clipBehavior propery of AlertDialog to Clip.hardEdge.

    Full Code : -

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          home: Box(),
        );
      }
    }
    
    class Box extends StatefulWidget {
      const Box({super.key});
    
      @override
      State<Box> createState() => _BoxState();
    }
    
    class _BoxState extends State<Box> {
      var selectedIndex = List<List>.generate(
          10, (i) => List<dynamic>.generate(10, (index) => false, growable: false),
          growable: false);
    
      Widget dialog() {
        return Center(
            child: ListView(children: [
          for (int i = 0; i < 5; i++) ...[
            StatefulBuilder(builder: (context, StateSetter setState) {
              return AlertDialog(
                clipBehavior: Clip.hardEdge,
                contentPadding: EdgeInsets.zero,
                titlePadding: EdgeInsets.zero,
                title: Container(
                  width: double.infinity,
                  height: 75,
                  decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.surfaceContainerHigh,
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(25),
                        topRight: Radius.circular(25),
                      )),
                  alignment: Alignment.center,
                  child: Text("Choose Emergency Action"),
                ),
                content: SizedBox(
                  width: double.maxFinite,
                  height: 300,
                  child: SingleChildScrollView(
                    child: Column(
                      children: [
                        for (int j = 0; j < 10; j++) ...[
                          ListTile(
                            selected: selectedIndex[i][j],
                            title: Text(
                              'Item : - $j',
                            ),
                            contentPadding: EdgeInsets.all(10),
                            selectedTileColor: Colors.purple[400],
                            onTap: () {
                              setState(() {
                                selectedIndex[i][j] = !selectedIndex[i][j];
                              });
                            },
                          ),
                        ]
                      ],
                    ),
                  ),
                ),
              );
            })
          ]
        ]));
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Center(
          child: GestureDetector(
              onTap: () {
                showDialog<String>(
                  context: context,
                  builder: (BuildContext context) => dialog(),
                );
              },
              child: Text('open dialog')),
        ));
      }
    }
    

    Output: -