简体   繁体   中英

How to create a Table of buttons in Flutter and randomly select any button from it?

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: () {},
            )
          ]
        ),
      ]
    ),
  )

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

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;
          });
        },
      )

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 ofList class but that's only applicable if you're storing your numbers inside a list.

Here's the output:

https://imgur.com/dkIeH2J

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()
                )
            )     
        );
    }
}

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