Search code examples
flutterdartflutter-getx

Can't get GetX work with Widget that do not have direct access to controller


GetX works fine and update data if element have controller. But how to get it work if we have not direct to controller and widget done in way of changing date with onChange.

I created small copy-paste example:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:web_date_picker/web_date_picker.dart';

void main() {
  Get.put(SearchFormController());
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'GetX Demo',
      home: MyHomePage(),
    );
  }
}

class SearchFormController extends GetxController {
  var endDate = Rxn<DateTime?>();

  setToNow() { // This function should set widget value
    endDate.value = DateTime.now();
    print('setToNow event: ${endDate.value}');
  }
}

class MyHomePage extends StatelessWidget {
  var ctrl = Get.find<SearchFormController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Row(
        children: [
          Obx(() => SizedBox(
                width: 160,
                child: WebDatePicker(
                  initialDate: ctrl.endDate.value,
                  onChange: (value) {
                    if (value != null) {
                      ctrl.endDate.value = value;
                      print('onChange event: ${ctrl.endDate.value}');
                    }
                  },
                ),
              )),
          ElevatedButton(
            onPressed: () {
              ctrl.setToNow();
            },
            child: Text("Set Date"),
          ),
        ],
      ),

    );
  }
}

pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  web_date_picker: ^1.0.0+3
  get: ^4.6.5  

In this code I can't set date by clicking on Set Date button.

I looked at widget code and it's controller is hidden with follow realization:

class _WebDatePickerState extends State<WebDatePicker> {
  final FocusNode _focusNode = FocusNode();

  late OverlayEntry _overlayEntry;

  final LayerLink _layerLink = LayerLink();

  final _controller = TextEditingController();

  late DateTime? _selectedDate;
  late DateTime _firstDate;
  late DateTime _lastDate;

  bool _isEnterDateField = false;

  @override
  void initState() {
    super.initState();

    _selectedDate = widget.initialDate;
    _firstDate = widget.firstDate ?? DateTime(2000);
    _lastDate = widget.lastDate ?? DateTime(2100);

    if (_selectedDate != null) {
      _controller.text = _selectedDate?.parseToString(widget.dateformat) ?? '';
    }

    _focusNode.addListener(() {
      if (_focusNode.hasFocus) {
        _overlayEntry = _createOverlayEntry();
        Overlay.of(context)?.insert(_overlayEntry);
      } else {
        _controller.text = _selectedDate.parseToString(widget.dateformat);
        widget.onChange.call(_selectedDate);
        _overlayEntry.remove();
      }
    });
  }

  void onChange(DateTime? selectedDate) {
    _selectedDate = selectedDate;
    _controller.text = _selectedDate.parseToString(widget.dateformat);

    _focusNode.unfocus();
  }
...

Widget code https://github.com/duchdtran/web_date_picker/blob/master/lib/src/web_date_picker.dart#L98

Could anybody provide example how to get button work to set widget value?


Solution

  • I experienced similar problems and solved them desribed as below.

    It seems WebDatePicker does not process the value change. Try putting it in its own StatelessWidget:

    class MyWebDatePicker extends StatelessClass {
      final DateTime dt;
      var ctrl = Get.find<SearchFormController>();
    
      MyWebDatePicker(this.dt);
    
      Widget build(BuildContext context) {
        return WebDatePicker(
                      initialDate: dt,
                      onChange: (value) {
                        if (value != null) {
                          ctrl.endDate.value = value;
                          print('onChange event: ${value}');
                        }
                      },
                    );
      }
    }
    

    Then, call it like given below. This code looks unusual, but it forces Obx to call MyWebDatePicker with the current DateTime value:

    Obx(() => SizedBox(
      width: 160,
      child: MyWebDatePicker(ctrl.endDate.value)
      )
    )