3

I am trying to create a custom Housie Game Ticket Generator in Flutter, in which I have to randomly select a button from a 4X3 table of buttons, and show a no. on that button. On clicking the button, it changes it's colour from Green to Red.

I have created a 4X3 table of buttons by hard coding all the cells as seen below. Now I want to select some random buttons from the table and set an onPressed function on them. How can I proceed?

   body: new Container(
    child: new Table(

      border: TableBorder.all(),
      children: [
        TableRow(
          children: [
            new FlatButton(
              color: Colors.white,
              onPressed: () {},

            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            )
          ]
        ),
        TableRow(
          children: [
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            )
          ]
        ),
        TableRow(
          children: [
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            ),
            new FlatButton(
              color: Colors.white,
              onPressed: () {},
            )
          ]
        ),
      ]
    ),
  )
Anmol Garg
  • 39
  • 1
  • 3

3 Answers3

3

Now I want to select some random buttons from the table and set an onPressed function on them. How can I proceed?

The safest place to store the button's "identity" be it first or 10th or 100th button is inside it.

class GameButton extends StatelessWidget {
  final int id;

  const GameButton({
     this.id,
     })
  ...

When the button is clicked , you want that info in the very moment of the click - let's make button tell us this info:

class GameButton extends StatelessWidget {
      final int id;
      final Function(int) onPressed;

      const GameButton({
         this.id,
         this.onPressed,
         })
      ...

Mind the Function(int) onPressed added here - it's a callback which passes out an integer, we'll call it when the button is clicked and let the button pass it's id to this function:

class GameButton extends StatelessWidget {
  final int id;
  final Function(int) onPressed;

  const GameButton({this.id, this.onPressed});

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      onPressed: () {
        // on click, we pass id to onPressed(int)
        onPressed(this.id);
      },
      child: null,
    );
  }
}

We'll define what to do with this id when creating each button:

...
new GameButton(
            id: id,
            onPressed: onButtonClicked,
          ),
...

To create table of buttons you can first write them to List<TableRow>, fill each row with desired number of buttons, and then set the whole list of rows as children to Table:

List<TableRow> buildButtons() {
    // list for all rows
    List<TableRow> rows = [];

    // each button get's unique id
    int id = 0;

    for (var i = 0; i < widget.rows; i++) {
      // new empty row
      List<Widget> rowChildren = [];

      // ---------------------- id incremented here
      for (var y = 0; y < widget.cols; y++,id++) {
        // fill row with buttons
        rowChildren.add(
          new GameButton(
            id: id,
            onPressed: onButtonClicked,
          ),
        );
      }
      rows.add(new TableRow(children: rowChildren));
    }
    return rows;
  }

And here is the handler:

onButtonClicked(int id) {
    // this id ^ variable is the one coming from any clicked button
    // use it e.g. to compare with any other variables from State
    print("clicked button $id");
  }

This is the code to get random number:

int max = widget.rows * widget.cols - 1;
this.randomSelection =
          Random.secure().nextInt(max);

Final result might look like :

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

class ButtonTable extends StatefulWidget {
  final int rows;
  final int cols;

  const ButtonTable({Key key, this.rows: 6, this.cols: 4}) : super(key: key);

  get max => rows * cols - 1;

  @override
  _ButtonTableState createState() => _ButtonTableState();
}

class _ButtonTableState extends State<ButtonTable> {
  int randomNumber = -1;

  List<int> pot;

  List<int> crossedNumbers;
  List<int> initialTicket;

  String resultText = "";
  String statusText = "Roll it";

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

  void restart() {
    initialTicket = generateTicket();
    pot = List.generate(widget.max, (index) => index);
    crossedNumbers = [];

    randomNumber = -1;
  }

  List<int> generateTicket() {
    var temp = List.generate(widget.max, (index) => index);

    List<int> ticket = [];
    for (int i = 0; i < widget.max / 2; i++) {
      final randomIndex = Random.secure().nextInt(temp.length);

      ticket.add(temp.removeAt(randomIndex));
    }
    return ticket;
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Column(
      children: <Widget>[
        new Table(
          border: TableBorder.all(),
          children: buildButtons(),
        ),
        Text("$statusText"),
        Text("$resultText"),
        Center(
          child: Row(
            children: <Widget>[
              FlatButton(
                color: Colors.grey,
                onPressed: rollNext,
                child: Text("Roll"),
              ),
              FlatButton(
                  color: Colors.grey,
                  onPressed: () {
                    setState(() {
                      restart();
                    });
                  },
                  child: Text("Restart")),
            ],
          ),
        ),
        Text("Pot:" + pot.toString())
      ],
    ));
  }

  onButtonClicked(id) {
    setState(() {
      if (id == randomNumber) {
        if (isNumberPlaying(id)) {
          resultText = Random.secure().nextBool() ? "Housie" : "Whoo";
          statusText = "Pull next number";
          crossedNumbers.add(id);
        } else {
          resultText = Random.secure().nextBool()
              ? "You can't cheat machine code"
              : "Nice try, but you don't have it on your ticket!";
        }
      } else {
        resultText =
            Random.secure().nextBool() ? "Missed, are u ok?" : "Try harder";
      }
    });
  }

  List<TableRow> buildButtons() {
    List<TableRow> rows = [];

    int id = 0;

    for (var i = 0; i < widget.rows; i++) {
      // new empty row
      List<Widget> rowChildren = [];

      for (var y = 0; y < widget.cols; y++, id++) {
        // fill row with buttons
        rowChildren.add(
          new GameButton(
            id: id,
            playing: isNumberPlaying(id),
            crossed: isCrossed(id),
            onPressed: onButtonClicked,
          ),
        );
      }
      rows.add(new TableRow(children: rowChildren));
    }

    return rows;
  }

  rollNext() {
    setState(() {
      if (pot.length > 0) {
        int randomIndex = Random.secure().nextInt(pot.length);

        this.randomNumber = pot.removeAt(randomIndex);

        this.statusText = "Rolled: $randomNumber";
        this.resultText = "playing one more time...";
      } else {
        restart();
      }
    });
  }

  isNumberPlaying(int id) {
    return initialTicket.contains(id);
  }

  isCrossed(int id) {
    return crossedNumbers.contains(id);
  }
}

class GameButton extends StatelessWidget {
  final int id;
  final Function(int) onPressed;
  final bool playing;
  final bool crossed;

  const GameButton({
    Key key,
    this.id,
    this.onPressed,
    this.playing,
    this.crossed,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FlatButton(
      color: decideColor(),
      onPressed: () {
        onPressed(this.id);
      },
      child: Stack(
        children: <Widget>[
          Visibility(
              visible: crossed,
              child: Icon(
                Icons.done,
                size: 48,
                color: Colors.brown,
              )),
          decideText()
        ],
      ),
    );
  }

  Color decideColor() {
    // if id is not active = white
    if (!this.playing)
      return Colors.white;
    else if (this.crossed) {
      return Colors.yellow;
    }
  }

  decideText() {
    return Text(
      playing ? "$id" : '',
      style: TextStyle(
        color: crossed ? Colors.green : Colors.black,
        fontWeight: crossed ? FontWeight.bold : FontWeight.normal,
      ),
    );
  }
}

Have fun

0

I don't get your question clearly, Can you explain it more ? but you can change buttons color like this.

declare a variable : bool didColourChange = false;

FlatButton(
        color: didColourChange ? Colors.red: Colors.green,
        onPressed: () {
          setState(() {
            didColourChange = !didColourChange;
          });
        },
      )
cipli onat
  • 1,943
  • 1
  • 11
  • 26
0

If you wanted to add some animations, you can use AnimatedContainer widget together with GestureDetector instead of using FlatButton widget.

To avoid hard coding your children, you might want to use the map method of List class but that's only applicable if you're storing your numbers inside a list.

Here's the output:

https://i.stack.imgur.com/o54gl.jpg

Here's the full code:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
                primarySwatch: Colors.blue,
            ),
            home: MyHomePage(title: 'Flutter Demo Home Page'),
        );
    }
}

class MyHomePage extends StatefulWidget {
    MyHomePage({Key key, this.title}) : super(key: key);

    final String title;

    @override
    _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


    List<List<int>> someListsOfNumbers = [
        List.generate(4, (int idx) => idx),
        List.generate(4, (int idx) => idx + 4),
        List.generate(4, (int idx) => idx + 8),
        ];


    Map<int, bool> pressedValues = Map.fromIterable(
        List.generate(12, (int idx) => idx), 
        key: (item) => item, 
        value: (item) => false,
        );

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text(widget.title),
            ),
            body: new Container(
                child: new Table(
                    border: TableBorder.all(),
                    children: someListsOfNumbers.map(
                        (List<int> someList) => TableRow(
                            children: someList.map((int val) => GestureDetector(
                                onTap: (){
                                    setState((){
                                        pressedValues[val] = !pressedValues[val];
                                    });
                                },  
                                child: AnimatedContainer(
                                    duration: const Duration(milliseconds: 700),
                                    height: 56.0,
                                    color: pressedValues[val] ? Colors.red : Colors.green,
                                    child: Center(
                                        child: pressedValues[val] ? Text(val.toString()) : Text(""),
                                        )
                                    )
                                )
                            ).toList()
                        )
                    ).toList()
                )
            )     
        );
    }
}
Jerome Escalante
  • 4,027
  • 2
  • 17
  • 17