1

I am passing the method as follows But it is very complicated. I want to call methods directly without passing methods. Is there any way to do that?

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

  @override
  Widget build(BuildContext context) {
    return Child(onPressed: onPressed);
  }

  onPressed() {
    print("onPressed");
  }
}

class Child extends StatelessWidget {
  const Child({
    Key? key,
    required this.onPressed,
  }) : super(key: key);

  final Function() onPressed;

  @override
  Widget build(BuildContext context) {
    return Grandchild(onPressed: onPressed);
  }
}

class Grandchild extends StatelessWidget {
  const Grandchild({
    Key? key,
    required this.onPressed,
  }) : super(key: key);

  final Function() onPressed;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.black,
      mini: true,
      child: const Icon(
        Icons.cached,
        color: Colors.white,
      ),
      tooltip: 'Change Camera',
      onPressed: onPressed,
    );
  }
}

I'm envisioning something like Riverpod's StateNotifierProvider, etc. where you pass methods like you pass properties, but if you can make it easier, that's fine too.

Ganessa
  • 782
  • 2
  • 7
  • 24
  • You should check InheritedWidget: https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html. This is exactly what it is used for. Or to make it simpler - check the Provider package https://pub.dev/packages/provider - this one is a wrapper around InheritedWidget and makes it really easy to use. – Andrija Oct 28 '22 at 04:47
  • you can call the function of your grandChild from your parent by using GlobalKey. if you want a demo let me know. – john Oct 28 '22 at 05:07
  • @Andrija Thanks for the comment, can I call methods of another class with InheritedWidget or Provider? Checked but i didn't find it. Can you show me a simple sample? – Ganessa Oct 28 '22 at 05:17
  • @john I tried to find out how to use GlobalKey but I could not figure out how to call a method of another class with it. Can you please share a simple demo? – Ganessa Oct 28 '22 at 05:18
  • globalKey works only to access child and grandchild method but not to access parent methods. inherited widget is the way to do that but with the help of ChangeNotifier of StateNotifier, you can save a lot of boilerplate. – john Oct 28 '22 at 06:19
  • @john I am asking how to call a method defined in the Parent class from the Grandchild class in the example I showed. – Ganessa Oct 28 '22 at 10:02
  • i got it..i'll give you a working code in the answer box. together on how to call grandchild method from parent using globalKey. – john Oct 28 '22 at 11:02

3 Answers3

0

You don't have to use StateNotifierProvider if you don't have a state. Use Provider:

final parentProvider = Provider<ParentNotifier>((ref) {
  return ParentNotifier();
});

class ParentNotifier {
  ParentNotifier();

  onPressed() {
    print("onPressed");
  }
}

and in ui:

main() => runApp(const ProviderScope(child: MaterialApp(home: Parent())));

class Parent extends ConsumerWidget {
  const Parent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return const Child();
  }
}

class Child extends ConsumerWidget {
  const Child({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return const Grandchild();
  }
}

class Grandchild extends ConsumerWidget {
  const Grandchild({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return FloatingActionButton(
      backgroundColor: Colors.black,
      mini: true,
      tooltip: 'Change Camera',
      onPressed: () => ref.read(parentProvider).onPressed(),
      child: const Icon(Icons.cached, color: Colors.white),
    );
  }
}

Notice for classes Child() and Grandchild() now you can use const.

Ruble
  • 2,589
  • 3
  • 6
  • 29
0

You can try this in Dartpad. Note that I converted your Parent to a simple class that only has a method you want child widgets to access.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Provider<ModelFromParent>(
                  create: (_) => ModelFromParent(),
                  child: const Parent()
          )
        ),
      ),
    );
  }
}



class ModelFromParent {
  onPressed() {
    print("onPressed");
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Child();
  }
  
}

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

  
  @override
  Widget build(BuildContext context) {
    return const Grandchild();
  }
}

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

  

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.black,
      mini: true,
      tooltip: 'Change Camera',
      onPressed: Provider.of<ModelFromParent>(context, listen:false).onPressed,
      child: const Icon(
        Icons.cached,
        color: Colors.white,
      )
    );
  }
}

Here's another take - in case you wish to use the Parent Widget - as you mentioned in the comments.

You have two options: make parent an InheretedWidget. But note - in this case Parent has no build method, and is used only as a proxy to build a child. This may or may not work for you. Second option is to call a static method of the parent - but in this case you don't have an access to Parent state and variables initialized in constructor. Again - this may or may not work for you.

I think if none of the options are applicable - you might be trying to achieve an anti-pattern. Typical approach would be my first suggestion - create a model class from you parent, and pass it using Provider or a similar package.

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: Parent(child: Child())
          )
        ),
    );
  }
}





class Parent extends InheritedWidget {
  const Parent({super.key, required super.child});

  
  static Parent of(BuildContext context) {
    final Parent? result = context.dependOnInheritedWidgetOfExactType<Parent>();
    assert(result != null, 'No Parent found in context');
    return result!;
  }
  @override
  bool updateShouldNotify(Parent old) => false;
  
  onPressed() {
    print("onPressed");
  }
  
  static void staticOnPressed() {
    print("staticOnPressed");
  }
  
}

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

  
  @override
  Widget build(BuildContext context) {
    return const Grandchild();
  }
}

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

  

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      backgroundColor: Colors.black,
      mini: true,
      tooltip: 'Change Camera',
      onPressed: () {
        Parent.of(context).onPressed();
        Parent.staticOnPressed();
      },
      child: const Icon(
        Icons.cached,
        color: Colors.white,
      )
    );
  }
}
Andrija
  • 1,534
  • 3
  • 10
  • In this example, it appears that the Parent class is not the parent of the Child class. Is it not feasible with the parent/child/grandchild structure I have indicated? – Ganessa Oct 28 '22 at 10:04
  • Of course you can, I changed the code to reflect this. The idea is that your function goes to a separate object that is above all your widgets in the tree. – Andrija Oct 28 '22 at 10:19
  • I seem not to have communicated my question to you well. I want to call a method of the Parent class, not the ModelFromParent class. – Ganessa Oct 28 '22 at 11:16
  • I added one more example in the answer above – Andrija Oct 31 '22 at 05:16
  • Maybe this is what we were hoping for. We will give this a shot. – Ganessa Oct 31 '22 at 11:28
0

here is an example of how to call a parent method from grandchild. Hope this answers your question.

class ParentScreen extends StatelessWidget {
  ParentScreen({super.key});



  void parentMethod() {
    print('calling parent method...');
  }

  @override
  Widget build(BuildContext context) {
    print('building parent ...');
    return Scaffold(
      appBar: AppBar(title: const Text('home page')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
          const  ChildWidget(
      
            )
          ],
        ),
      ),
    );
  }
}

class ChildWidget extends StatefulWidget {
  const ChildWidget({super.key, required this.grandChildKey});


  @override
  State<ChildWidget> createState() => ChildWidgetState();
}

class ChildWidgetState extends State<ChildWidget> {



  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.amber,
      child: Center(
        child: Column(
          children: [
            const Text(
              'child',
            ),
           const GrandChild(
      
            )
          ],
        ),
      ),
    );
  }
}

class GrandChild extends StatefulWidget {
  const GrandChild({
    super.key,
  });

  @override
  State<GrandChild> createState() => GrandChildState();
}

class GrandChildState extends State<GrandChild> {
  void callParent() {
  //for stateless parent:
    final parent = ParentScreen();
    parent.parentMethod();
    
    //if the parent is stateful:
//final parent = ParentScreenState();
//parent.parentMethod();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
            color: Colors.amber,
            child: const Center(child: Text('grand child'))),
        ElevatedButton(onPressed: callParent, child: const Text('call parent'))
      ],
    );
  }
}
john
  • 1,438
  • 8
  • 18
  • Thank you. But I didn't understand your code, but perhaps you are passing a key instead of a method. I would rather pass a method than do that. Instead I am asking about how to call the method directly. I thought there was an easier way but no one answered me about it. Perhaps there is no such method. – Ganessa Oct 28 '22 at 11:23
  • let me edit to remove the extra methods...those are just additional but let me focus it on calling the parent from grandchild to make it clear. – john Oct 28 '22 at 11:30
  • if it is still unclear, maybe others can explain it better. – john Oct 28 '22 at 11:37
  • Do you create a separate instance from the parent ParentScreen? Unfortunately this does not seem to be what we are expecting. – Ganessa Oct 31 '22 at 11:25