Ad

Flutter Grid Of Buttons That Redirects To Other Page When Clicked

- 1 answer

Hi guys i am new to flutter. I have a grid of clickable buttons. Right now its only two, but it can grow to many. how can I refactor this to make it more dynamic and handle many future buttons? like a grid list of buttons that you can each click to navigate to different pages.

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Container(
        margin: EdgeInsets.all(10),
        padding: EdgeInsets.all(30.0),
        child: GridView.extent(
          maxCrossAxisExtent: 150,
          crossAxisSpacing: 15.0,
          mainAxisSpacing: 15.0,
          children: <Widget>[
            FlatButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (_) => ProductScreen(),
                  ),
                );
              },
              padding: EdgeInsets.only(top: 33),
              child: Column(
                children: [
                  Icon(
                    Icons.shopping_cart_outlined,
                    color: Colors.white,
                  ),
                  Text(
                    "Products",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),
                ],
              ),
            ),
            FlatButton(
                 onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (_) => MailScreen(),
                  ),
                );
              },
              padding: EdgeInsets.only(top: 33),
              child: Column(
                children: [
                  Icon(
                    Icons.mail,
                    color: Colors.white,
                  ),
                  Text(
                    "Mail",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Ad

Answer

Here is a solution:

1. Define a AppAction Model

class AppAction {
  final Color color;
  final String label;
  final Color labelColor;
  final IconData iconData;
  final Color iconColor;
  final void Function(BuildContext) callback;

  AppAction({
    this.color = Colors.blueGrey,
    this.label,
    this.labelColor = Colors.white,
    this.iconData,
    this.iconColor = Colors.white,
    this.callback,
  });
}

You could also have the route or its name instead of a callback function. Though, a callback will allow you to define other types of actions if needed. (example: launching an external URL, triggering a modal dialog, etc.)

2. Defining your Application Actions

final List<AppAction> actions = [
  AppAction(
    label: 'Products',
    iconData: Icons.shopping_cart_outlined,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => ProductScreen()));
    },
  ),
  AppAction(
    label: 'Mails',
    iconData: Icons.mail,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => MailScreen()));
    },
  ),
  AppAction(
    color: Colors.white,
    label: 'Urgent',
    labelColor: Colors.redAccent,
    iconData: Icons.dangerous,
    iconColor: Colors.redAccent,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => UrgentScreen()));
    },
  ),
  AppAction(
    color: Colors.green.shade200,
    label: 'News',
    labelColor: Colors.black,
    iconData: Icons.new_releases,
    iconColor: Colors.green,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => NewsScreen()));
    },
  ),
];

3. Define a generic ActionButton

class ActionButton extends StatelessWidget {
  final AppAction action;

  const ActionButton({
    Key key,
    this.action,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return OutlinedButton.icon(
      onPressed: () => action.callback?.call(context),
      style: OutlinedButton.styleFrom(
        backgroundColor: action.color,
        padding: const EdgeInsets.all(16.0),
      ),
      label: Text(action.label, style: TextStyle(color: action.labelColor)),
      icon: Icon(action.iconData, color: action.iconColor),
    );
  }
}

4. Simplify your HomePage

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: 'Home Page',
      child: Container(
        margin: EdgeInsets.all(10),
        padding: EdgeInsets.all(30.0),
        child: GridView.extent(
          maxCrossAxisExtent: 120,
          crossAxisSpacing: 15.0,
          mainAxisSpacing: 15.0,
          children: actions.map((action) => ActionButton(action: action)).toList(),
        ),
      ),
    );
  }
}

Voilà! If you want, here is a full standalone code sample to play with:

enter image description here

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    ),
  );
}

class AppAction {
  final Color color;
  final String label;
  final Color labelColor;
  final IconData iconData;
  final Color iconColor;
  final void Function(BuildContext) callback;

  AppAction({
    this.color = Colors.blueGrey,
    this.label,
    this.labelColor = Colors.white,
    this.iconData,
    this.iconColor = Colors.white,
    this.callback,
  });
}

final List<AppAction> actions = [
  AppAction(
    label: 'Products',
    iconData: Icons.shopping_cart_outlined,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => ProductScreen()));
    },
  ),
  AppAction(
    label: 'Mails',
    iconData: Icons.mail,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => MailScreen()));
    },
  ),
  AppAction(
    color: Colors.white,
    label: 'Urgent',
    labelColor: Colors.redAccent,
    iconData: Icons.dangerous,
    iconColor: Colors.redAccent,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => UrgentScreen()));
    },
  ),
  AppAction(
    color: Colors.green.shade200,
    label: 'News',
    labelColor: Colors.black,
    iconData: Icons.new_releases,
    iconColor: Colors.green,
    callback: (context) {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (_) => NewsScreen()));
    },
  ),
];

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: 'Home Page',
      child: Container(
        margin: EdgeInsets.all(10),
        padding: EdgeInsets.all(30.0),
        child: GridView.extent(
          maxCrossAxisExtent: 120,
          crossAxisSpacing: 15.0,
          mainAxisSpacing: 15.0,
          children:
              actions.map((action) => ActionButton(action: action)).toList(),
        ),
      ),
    );
  }
}

class AppLayout extends StatelessWidget {
  final String pageTitle;
  final Widget child;

  const AppLayout({Key key, this.pageTitle, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(pageTitle)),
      body: child,
    );
  }
}

class ActionButton extends StatelessWidget {
  final AppAction action;

  const ActionButton({
    Key key,
    this.action,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return OutlinedButton.icon(
      onPressed: () => action.callback?.call(context),
      style: OutlinedButton.styleFrom(
        backgroundColor: action.color,
        padding: const EdgeInsets.all(16.0),
      ),
      label: Text(action.label, style: TextStyle(color: action.labelColor)),
      icon: Icon(action.iconData, color: action.iconColor),
    );
  }
}

class ProductScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: ('Products Page'),
      child: Center(
        child: Text('LIST OF PRODUCTS'),
      ),
    );
  }
}

class MailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: 'Mail Page',
      child: Center(
        child: Text('LIST OF MAIL'),
      ),
    );
  }
}

class UrgentScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: 'Urgent Page',
      child: Center(
        child: Text('URGENT', style: TextStyle(color: Colors.redAccent)),
      ),
    );
  }
}

class NewsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AppLayout(
      pageTitle: 'News Page',
      child: Center(
        child: Text('NEWS', style: TextStyle(color: Colors.green)),
      ),
    );
  }
}
Ad
source: stackoverflow.com
Ad