Search code examples
flutterdartflutter-layoutdart-null-safety

How can I set a Future Builder Function on a different dart file?


I have a Future (async) Date&Time picker function which works fine from within the body of my stateful widget which contains the "Builder" and the function can be called via the onpressed by just this:

onPressed: () {SelectDayAndTimeL();}, 

Code:

Future _selectDayAndTimeL(BuildContext context) async {
    DateTime _selectedDay = await showDatePicker(
        context: context,
        initialDate: DateTime.now(),
        firstDate: DateTime(2021),
        lastDate: DateTime(2030),
        builder: (BuildContext context, Widget child) => child);

    TimeOfDay _selectedTime = await showTimePicker(
      context: context,
      initialTime: TimeOfDay.now(),
    );

    if (_selectedDay != null && _selectedTime != null) {
      //a little check
    }
    setState(() {
      selectedDateAndTime = DateTime(
        _selectedDay.year,
        _selectedDay.month,
        _selectedDay.day,
        _selectedTime.hour,
        _selectedTime.minute,
      );
      // _selectedDate = _selectedDay;
    });
    // print('...');
  }

Now I want to be able to call this function from different dart files/screens which means I have to keep this function on a different dart file which I have tried to do, but because of the setState in the function it needs to be inside a stateful widget. I have tried putting it inside a stateful widget but keeps getting errors.

class Picker extends StatefulWidget {
  @override
  _PickerState createState() => _PickerState();
}

class _PickerState extends State<Picker> {
  @override
  Future<Widget> build(BuildContext context) async { //the error in on the build on this line
    DateTime _selectedDay = await showDatePicker(
        context: context,
        initialDate: DateTime.now(),
        firstDate: DateTime(2021),
        lastDate: DateTime(2030),
        builder: (BuildContext context, Widget child) => child);

    TimeOfDay _selectedTime = await showTimePicker(
      context: context,
      initialTime: TimeOfDay.now(),
    );

    if (_selectedDay != null && _selectedTime != null) {
      //a little check
    }
    setState(() {
      selectedDateAndTime = DateTime(
        _selectedDay.year,
        _selectedDay.month,
        _selectedDay.day,
        _selectedTime.hour,
        _selectedTime.minute,
      );
      // _selectedDate = _selectedDay;
    });
  }
}

enter image description here

How do I properly place the Future Function inside a stateful widget and how to call it on an onpressed?

I don't know if the title I gave this question is actually what it's supposed to be, but I don't know how else to put it.


Solution

  • The whole setState inside your method is the problem. Your method should do one thing: get a date and time from the user. And there it's responsibility ends.

    Future<DateTime> SelectDayAndTimeL(BuildContext context) async {
        DateTime _selectedDay = await showDatePicker(
            context: context,
            initialDate: DateTime.now(),
            firstDate: DateTime(2021),
            lastDate: DateTime(2030),
            builder: (BuildContext context, Widget child) => child);
    
        TimeOfDay _selectedTime = await showTimePicker(
          context: context,
          initialTime: TimeOfDay.now(),
        );
    
        if (_selectedDay != null && _selectedTime != null) {
          //a little check
        }
        return DateTime(
            _selectedDay.year,
            _selectedDay.month,
            _selectedDay.day,
            _selectedTime.hour,
            _selectedTime.minute,
          );
      }
    

    Now your onPressed becomes:

    onPressed: () async { 
         final pickedDatetime = await SelectDayAndTimeL(context); 
         setState(() { selectedDateAndTime = pickedDatetime });
    },
    

    You have sucessfully divided your code into the function that picks a thing and your widget, which updates after the thing is picked.

    The function that picks the date and time can now be reused in every other widget.