Search code examples
flutterriverpodflutter-date-range-picker

How to use Flutter Riverpod with Date Range Picker


I am trying to use Flutter Riverpod together with the Date Range Picker.

  • I have a list of Trips which when clicked on, open up the Trip Page.

Home page

  • On the trip page, the user can view the start / end dates

Trip page

  • They can then click on a button, which brings up the Date Range Picker widget

Date Range Picker

  • I want the user to be able to change the date range, and this be reflected on the screen (and eventually saved back against the Trip object)
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Riverpod DateRange Picker',
      home: HomeScreen(),
    );
  }
}

class Trip {
  final String name;
  final DateTimeRange dateTimeRange;
  Trip(this.name, this.dateTimeRange);
}

class HomeScreen extends ConsumerWidget {
  const HomeScreen({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    Set<Trip> trips = {
      Trip('Test 1',
          DateTimeRange(start: DateTime(2023, 1, 3), end: DateTime(2023, 1, 6)))
    };
    return Scaffold(
      appBar: AppBar(title: const Text('Riverpod DateRange Picker')),
      body: ListView(
        children: [
          for (final trip in trips)
            ListTile(
              title: Text(trip.name),
              onTap: () {
                Navigator.push(
                    context,
                    MaterialPageRoute<void>(
                        builder: (BuildContext context) =>
                            TripScreen(trip: trip)));
              },
            ),
        ],
      ),
    );
  }
}

class TripScreen extends ConsumerWidget {
  const TripScreen({Key? key, required this.trip}) : super(key: key);

  final Trip trip;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    void _showDateRangePicker() async {
      final DateTimeRange? result = await showDateRangePicker(
        context: context,
        locale: const Locale('en', 'GB'),
        initialDateRange: trip.dateTimeRange,
        saveText: 'Done',
        firstDate: DateTime(2022, 1, 1),
        lastDate: DateTime(2030, 12, 31),
      );

      if (result != null) {
        print(result);
      }
    }

    return WillPopScope(
        onWillPop: () {
          Navigator.pop(context);
          return Future.value(false);
        },
        child: Scaffold(
            backgroundColor: Colors.white,
            appBar: AppBar(
              title: Text(trip.name),
            ),
            body: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    IconButton(
                      icon: const Icon(Icons.calendar_month),
                      onPressed: () {
                        _showDateRangePicker();
                      },
                    ),
                    Text(
                        'Start: ${DateFormat('dd/MM/yyyy').format(trip.dateTimeRange.start)}'),
                    Text(
                        'End: ${DateFormat('dd/MM/yyyy').format(trip.dateTimeRange.end)}'),
                  ],
                ))));
  }
}

Any help would be much appriciated.

Thanks.

I've created this using Provider but I would like to use Riverpod.


Solution

  • I created "tripsProvider" instance of StateProvider that holds all trip data, like in your case only one value. Home screen watching that provider and displaying data in ListView, when user select item in list I pass index of item and watch that single item on new screen (copy of yours TripScreen). When user update the data of single item I update that list, so update is available on TripScreen and HomeScreen. I also still learning riverpod but hope my answer can help you.

    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:intl/intl.dart';
    
    void main() {
      runApp(
        const ProviderScope(
          child: MyApp(),
        ),
      );
    }
    
    final tripsProvider = StateProvider<List<Trip>>(
      (ref) => [
        Trip(
          'Test 1',
          DateTimeRange(start: DateTime(2023, 1, 3), end: DateTime(2023, 1, 6)),
        ),
      ],
    );
    
    class Trip {
      final String name;
      final DateTimeRange dateTimeRange;
    
      const Trip(this.name, this.dateTimeRange);
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'Riverpod DateRange Picker',
          home: HomeScreen(),
        );
      }
    }
    
    class HomeScreen extends ConsumerWidget {
      const HomeScreen({super.key});
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final trips = ref.watch(tripsProvider);
    
        return Scaffold(
          appBar: AppBar(title: const Text('Riverpod DateRange Picker')),
          body: ListView.builder(
            itemCount: trips.length,
            itemBuilder: (context, index) {
              Trip trip = trips[index];
    
              return ListTile(
                title: Text(trip.name),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute<void>(
                      builder: (BuildContext context) => TripScreen(index: index),
                    ),
                  );
                },
              );
            },
          ),
        );
      }
    }
    
    class TripScreen extends ConsumerWidget {
      int index;
    
      TripScreen({Key? key, required this.index}) : super(key: key);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final trip = ref.watch(tripsProvider)[index];
    
        void _showDateRangePicker() async {
          final DateTimeRange? result = await showDateRangePicker(
            context: context,
            locale: const Locale('en', 'GB'),
            initialDateRange: trip.dateTimeRange,
            saveText: 'Done',
            firstDate: DateTime(2022, 1, 1),
            lastDate: DateTime(2030, 12, 31),
          );
    
          if (result != null) {
            print(result);
            Trip updatedTrip = Trip(trip.name, DateTimeRange(start: result.start, end: result.end));
    
            ref.read(tripsProvider.notifier).update(
              (state) {
                List<Trip> updatedList = [];
                for (int i = 0; i < state.length; i++) {
                  if (i == index) {
                    updatedList.add(updatedTrip);
                  } else {
                    updatedList.add(state[i]);
                  }
                }
                return updatedList;
              },
            );
          }
        }
    
        return WillPopScope(
          onWillPop: () {
            Navigator.pop(context);
            return Future.value(false);
          },
          child: Scaffold(
            backgroundColor: Colors.white,
            appBar: AppBar(
              title: Text(trip.name),
            ),
            body: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  IconButton(
                    icon: const Icon(Icons.calendar_month),
                    onPressed: () {
                      _showDateRangePicker();
                    },
                  ),
                  Text('Start: ${DateFormat('dd/MM/yyyy').format(trip.dateTimeRange.start)}'),
                  Text('End: ${DateFormat('dd/MM/yyyy').format(trip.dateTimeRange.end)}'),
                ],
              ),
            ),
          ),
        );
      }
    }