简体   繁体   中英

Call a method inside custom widget

I have created a custom widget. It comprises of read only TextFormField with suffixed IconButton, API, Alert Dialog and callback function The widget can be in 2 states, set or reset.

One put the widget in set condition by IconButton on TextFormField, this will execute an API call and the returned data is displayed on TextFormField.
The widget is reset from the parent screens depending on some application requirement.

I have imported and used this custom widget in my various activities (screens). Their In my screen I wish clear my custom widget and I have created clear method.

I wish to know who will I call this clearWidget method.

If required I can clearWidget method to class GetTimeWidget extends StatefulWidget

enum TimeWidgetEvent { Start, Stop }

class GetTimeWidget extends StatefulWidget {
  Ref<String> time;
  final TimeWidgetEvent mode;
  final String label;
  const GetTimeWidget({
    required this.time,
    required this.mode,
    required this.label,
    Key? key,
  }) : super(key: key);

  @override
  State<GetTimeWidget> createState() => _GetTimeWidgetState();
}

class _GetTimeWidgetState extends State<GetTimeWidget> {
final TextEditingController controller;
  @override
  Widget build(BuildContext context) {
    return TextFormField(
      controller: controller,
      readOnly: true,
      //initialValue: ,
      decoration: InputDecoration(
        label: Text(widget.label),
        hintText: 'Please Get ${widget.label} from sever',
        suffixIcon: TextButton.icon(
          onPressed: () {
            //Execute API to get time
          },
          icon: (widget.mode == TimeWidgetEvent.Start)
              ? const Icon(Icons.play_circle)
              : const Icon(Icons.stop_circle),
          label: (widget.mode == TimeWidgetEvent.Start)
              ? const Text('Start')
              : const Text('Stop'),
        ),
        border: const OutlineInputBorder(),
      ),
      validator: (value) {
        if (value == null || value.isEmpty) {
          return 'Please Get ${widget.label} from server'; //Validation error
        }
        return null; //Validation Success
      },
    );
  }
  
  void clearWidget()
  {
    controller.clear();
    //Execute API
  }
}

I think you can't. because the state class is private, and every method in that class (_GetTimeWidgetState) cannot called externally. If I correctly understand what you want to do, is to change the internal state of _GetTimeWidgetState outside from this widget.

I think you can't. My suggest is to use one of the state managers that you can find for flutter, like Riverpod (my choice), or Cubit, Get/Getx, etc... In that manner you can read/change the internal state using the global state managed by the state manager.

For example, with Riverpod you can define a StateClass that handles your data:

final myProvider = StateNotifierProvider<MyStateNotifier, MyState>((ref) {
    return MyStateNotifier("someInitialDataInfo");
 });

class MyStateNotifier extends StateNotifier<MyState> {
      MyStateNotifier("someInitialDataInfo") : super( MyState("someInitialDataInfo"));
    void clear(String someDataInfo)  { state = MyState( someDataInfo) ;}
}

@immutable
class MyState {
 ..... }

Then in your ComsumerState ( in Riverpod you should use ConsumerStatefulWidget and ConsumerState) you can watch the notifier as here:

class _GetTimeWidgetState extends ConsumerState<GetTimeWidget> {
   final TextEditingController controller;

@override
  Widget build(BuildContext context, WidgetRef ref) {
   final myState = ref.watch(myProvider );
   if ( myState.someDataInfo == 'Clicked Reset!!!!' ) {
        controller.clear();
   }
   return TextFormField( .... );
 }
 .... } ...}

Now, observe that the build method will be called when the state inside the Notifier class would change. Thus you will be notified once per change. Inside the StateNotifier class (the class you use to extend and to define your MyStateNotifier class) will do the following match to put your widget in the dirty-state:

state != oldState

That means that every time you change the internal state field, it will put your widget to the the dirty state, and thus it will be re builded. the MyState class is defined as @immutable , so every state change cannot not be done with something like:

 state.setMyField ( ' my value ' );

but will be done changing the state object itself:

state =  MyState ( ... );

or with its copy method:

 state = state.copyWith( .... ) ;

In this manner you avoid some side-effects ( the state should always be immutable )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM