3

In the code below, I have a method myMenu on a card. How do I navigate to another page when the card is tapped? There are going to be several of these cards which will link to its own page content. Each time I add a function to for an example it gives an error. How do I do it properly?

import 'package:flutter/material.dart';
import 'package:tarjous_app/gridview_demo.dart';

void main(List<String> args) {
  runApp(
      new MaterialApp(home: TarjousAle(), debugShowCheckedModeBanner: false));
}

class TarjousAle extends StatefulWidget {
  @override
  _TarjousAleState createState() => _TarjousAleState();
}

class _TarjousAleState extends State<TarjousAle> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: Text("Study Plan"),
        backgroundColor: Colors.amber,
      ),
      body: Container(
        child: GridView.count(
          crossAxisCount: 3,
          children: <Widget>[
            MyMenu(
              title: "Records",
              icon: Icons.account_balance_wallet,
              shape: Colors.brown,
            ),
            MyMenu(
              title: "Academy",
              icon: Icons.account_balance,
              shape: Colors.grey,
            ),
          ],
        ),
      ),
    );
  }
}

class MyMenu extends StatelessWidget {
  MyMenu({this.title, this.icon, this.shape});

  final String title;
  final IconData icon;
  final MaterialColor shape;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(9.0),
      child: InkWell(           
      onTap: () => Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => GridViewDemo()),
      ),
        splashColor: Colors.amberAccent,
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Icon(
                icon,
                size: 80.0,
                color: shape,
              ),
              Text(title, style: new TextStyle(fontSize: 18.0))
            ],
          ),
        ),
      ),
    );
  }
}

In the inkwell widget, I add a function that works for all the cards. But what I really want it for each card to navigate to its own page. E.g Records should navigate to its own records page, the same thing for Academy to academy page

Esiri
  • 45
  • 1
  • 7
  • `"How do I navigate to another page when the card is tapped?"` - see `Navigator` and `NavigatorState` classes documentation - also [Navigation & routing](https://flutter.dev/docs/development/ui/navigation) can be helpful – pskink Oct 31 '19 at 07:44
  • I get your explanation, but what I really need it to make each of the cards navigate to its own page. Thanks – Esiri Oct 31 '19 at 10:52

5 Answers5

5

You could receive the page in the constructor and then go to that page, like this:

class MyMenu extends StatelessWidget {
  MyMenu({this.title, this.icon, this.shape, this.page});

  final Widget page;
  ...
}

Then, in onTap:

onTap: () => Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => page),
)

So now you can do this:

MyMenu(
  ...
  page: GridViewDemo1(),
),
MyMenu(
  ...
  page: GridViewDemo2(),
)
Pablo Barrera
  • 10,387
  • 3
  • 28
  • 49
  • I tried your code too but the widget.page gives an error in the onTap method – Esiri Nov 01 '19 at 08:58
  • it says " The getter 'page' isn't defined for the class 'Widget'. Try importing the library that defines 'page', correcting the name to the name of an existing getter, or defining a getter or field named 'page'.dart(undefined_getter) " – Esiri Nov 01 '19 at 11:56
  • Because when the widget.page was in small letters it gives this: Undefined name 'widget'. Try correcting the name to one that is defined, or defining the name.dart(undefined_identifier) But then when I changed it to caps ( Widget.page) then it gives the other error – Esiri Nov 01 '19 at 11:58
  • I didn't realize you're using StatelessWidget instead of StatefulWidget, so you should directly use 'page' instead of 'widget.page' – Pablo Barrera Nov 01 '19 at 12:31
  • Nice, now it works. Thanks. I have really learnt a lot from you guys already. I am grateful – Esiri Nov 01 '19 at 13:54
  • Hi Pablo, so sorry if I am disturbing. I have been trying since yesterday to add photos to the individual cards to replace the icons. Meaning: if photo is available, it should be displayed in place of the icon. If no photo, then the icon. Like the page, I want to add the photos or pictures. Any suggestions? – Esiri Nov 02 '19 at 14:06
  • You could create a method that returns the Icon if there's no picture or the Image if there's a picture, then use that method where you want to show that. – Pablo Barrera Nov 02 '19 at 14:52
  • I have tried but seems to be getting it wrong. I wish I could show you the whole work. if you could show me some guide, it will be great. – Esiri Nov 02 '19 at 14:59
  • The method could be: Widget buildPic() => picture != null ? Image.asset(picture) : Icon(...); then use it like this: Column(children: [buildPic(), Text(...)]) – Pablo Barrera Nov 02 '19 at 15:08
  • this was what i did before : Widget getWidget(imagePath){ return Container( child: Column( children : [ Image.asset(imagePath), ] ) ); } – Esiri Nov 02 '19 at 15:32
  • Hi Esiri, create a new question explaining what are you trying to achieve, what problem are you facing and what error are you getting, so that anyone can help you. – Pablo Barrera Dec 04 '19 at 14:54
2

Note that to navigate to some page, your context must contain a Navigator instance of parent. So if you try to navigate directly from MaterialApp, you might run into issues. I will not belabour the point here since it was explained very well in this thread, but it is something to keep in mind in case you happen to run into it.

Edited to address comments:

I'd do something like this for your case. Named routes make it easy to specify which route you'd like the card to take you to, which you kind of need to do if you want the same widget to take you to different routes.

import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(
    new MaterialApp(
      home: TarjousAle(),
      debugShowCheckedModeBanner: false,
      routes: {
        GridViewDemo.route: (context) => GridViewDemo(),
        AnotherDemo.route: (context) => AnotherDemo(),
      },
    ),
  );
}

class TarjousAle extends StatefulWidget {
  @override
  _TarjousAleState createState() => _TarjousAleState();
}

class _TarjousAleState extends State<TarjousAle> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: Text("Study Plan"),
        backgroundColor: Colors.amber,
      ),
      body: Container(
        child: GridView.count(
          crossAxisCount: 3,
          children: <Widget>[
            MyMenu(
              title: "Records",
              icon: Icons.account_balance_wallet,
              shape: Colors.brown,
              route: GridViewDemo.route
            ),
            MyMenu(
              title: "Academy",
              icon: Icons.account_balance,
              shape: Colors.grey,
              route: AnotherDemo.route
            ),
          ],
        ),
      ),
    );
  }
}

class MyMenu extends StatelessWidget {
  MyMenu({this.title, this.icon, this.shape, this.route});

  final String title;
  final IconData icon;
  final MaterialColor shape;
  final String route;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(9.0),
      child: InkWell(
        onTap: () => Navigator.pushNamed(context, route),
        splashColor: Colors.amberAccent,
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Icon(
                icon,
                size: 80.0,
                color: shape,
              ),
              Text(title, style: new TextStyle(fontSize: 18.0))
            ],
          ),
        ),
      ),
    );
  }
}

class GridViewDemo extends StatelessWidget {
  static String route = '/demo';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.brown,
      appBar: AppBar(title: Text('Grid view demo')),
      body: Center(
        child: Text('Grid view demo'),
      ),
    );
  }
}

class AnotherDemo extends StatelessWidget {
  static String route = '/another';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(title: Text('Another demo')),
      body: Center(
        child: Text('Another demo'),
      ),
    );
  }
}

You can read more about the basics of navigation in official docs, and also another docs page if you fancy the named routes.

cegas
  • 2,823
  • 3
  • 16
  • 16
  • Please check the edited code and my comments above. Thanks – Esiri Oct 31 '19 at 10:58
  • I've updated the code to address the case you outlined - I hope that makes it bit clearer. – cegas Oct 31 '19 at 11:54
  • Yes, thanks it worked. But I do not understand why I have to declare the route in the GridViewDemo class before it could work. Any explanation, please. – Esiri Nov 01 '19 at 07:46
  • If you're talking about a string (`/demo`), that's not necessary - you can define the routes wherever it is convenient for you (e.g., a separate file, or just hardcode the strings where needed). I did it so that everything related to a particular widget would be in one place. However, it is definitely not a hard requirement. Sorry if that caused any confusion! – cegas Nov 01 '19 at 14:22
  • no, its fine sir. I just wanted to know if it required or not. Thanks. – Esiri Nov 02 '19 at 12:42
1

Wrap the card with GestureDetector and you can use opnTap property. for more details Official Documentation

Riyaz Shaikh
  • 141
  • 9
0

wrap the card with InkWell widget and define your navigator.push in the onTap method.

    class CardWidget extends StatelessWidget {
      final Function onTapCard;

      const CardWidget({Key key, @required this.onTapCard}) : super(key: key);
      @override
      Widget build(BuildContext context) {
        return Card(
          margin: EdgeInsets.all(9.0),
          child: InkWell(
            onTap: onTapCard,
            splashColor: Colors.amberAccent,
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Icon(
                    icon,
                    size: 80.0,
                    color: shape,
                  ),
                  Text(title, style: new TextStyle(fontSize: 18.0))
                ],
              ),
            ),
          ),
        );
      }

}

then we have our list here

class CardList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        CardWidget(
          onTapCard: () => Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => YourSecondPage()),
          ),
        ),
        CardWidget(
          onTapCard: Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => YourThirdPage()),
          ),
        ),
      ],
    );
  }
}
Salma
  • 1,211
  • 2
  • 16
  • 33
  • In the edited inkwell widget, every one of the cards navigates to the "GridViewDemo()" page. But I want each of them to navigate to its own page. Is there a way I can add a function to each card ( MyMenu( title: "Academy", icon: Icons.account_balance, shape: Colors.grey,) I am still very new to flutter, so please pardon me – Esiri Oct 31 '19 at 10:55
  • if you have an indefinite number of cards you can create a Listview.builder where u just return the cards. but if you have a definite number of cards you can create a listview, row or column with a card widget. separate the card in another class and then pass the onTap as a function – Salma Oct 31 '19 at 11:13
0

Try wrapping your Card in a GestureDetector like below:

GestureDetector (
child: Card(),
onTap: () {},
),