Flutter : I want to change an image when you tap the image, and others are not affected by the tap

Issue

I am creating a simple app in Flutter. There are 7 images on 1 screen. I need a function that you can change an image when you tap one of the images. However, now when I tap an image, the other 6 images are also changed. I made a variable "isReal" to put into buildButton() and "isReal" would be switched true and false in the For statement which switch "isReal" in buildButton(). But, that did not work. Could you give me some advice on this problem? Thank you.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';

class Screen extends StatefulWidget {
  @override
  _ScreenState createState() => _ScreenState();
}

class _ScreenState extends State<Screen> {
  bool isReal = true;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
            backgroundColor: Colors.teal[100],
            // appBar: AppBar(
            //     title: Text('AnimalSounds'), backgroundColor: Colors.teal),
            body: SafeArea(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  buildButton('cat.mp3', Colors.red, 'images/cat.png',
                      'images/cat_real.jpg'),
                  Expanded(
                      child: Row(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: <Widget>[
                        buildButton('dog.mp3', Colors.yellow, 'images/dog.png',
                            'images/cow.png'),
                        buildButton('cow.mp3', Colors.orange, 'images/cow.png',
                            'images/dog.png'),
                      ])),
                  Expanded(
                      child: Row(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: <Widget>[
                        buildButton('pig.mp3', Colors.green, 'images/pig.png',
                            'images/elephant.png'),
                        buildButton('elephant.mp3', Colors.teal,
                            'images/elephant.png', 'images/rooster.png'),
                        buildButton('rooster.mp3', Colors.blue,
                            'images/rooster.png', 'images/pig.png'),
                      ])),
                  Expanded(
                      child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: <Widget>[
                      buildButton('goat.mp3', Colors.purple, 'images/goat.jpg',
                          'images/pig.png'),
                    ],
                  )),
                ],
              ),
            )));
  }

  Expanded buildButton(sound, color, simpleImage, realImage) {
    return Expanded(
        child: FlatButton(
      onPressed: () {
        setState(() {
          isReal = !isReal;
        });
        final player = AudioCache();
        player.play(sound);
      },
      color: color,
      child: isReal ? Image.asset(simpleImage) : Image.asset(realImage),
    ));
  }
}

Solution

Ok, you have variable isReal that is the same for entire class (i.e. each button use the same variable). So when you change it’s value by tapping on one button it affects all other buttons as well.

To solve this issue I would recommend to move button implementation into a separate Statefull widget. This way you can keep your Screen class as Stateless.

UPD:
Obviously you should watch some tutorials on how to make this on your own. But just for this time this is how it should look like after you separate widgets.

What I did here is:

  1. Create new widget class FlipButton
  2. Move code from your method into build function of new widget
  3. Add parameters to constructor

This way when each FlipButton will have it’s own isReal variable.

NOTE: I didn’t try to compile it so there might be some errors.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';

class Screen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        backgroundColor: Colors.teal[100],
        body: SafeArea(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              //replace all occurances on `buildButton` method with new widget
              FlipButton(sound: 'cat.mp3', color: Colors.red, simpleImage: 'images/cat.png', realImage: 'images/cat_real.jpg'),
              Expanded(
                  child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[
                FlipButton(sound: 'dog.mp3', color: Colors.yellow, simpleImage: 'images/dog.png', realImage: 'images/cow.png'),
                FlipButton(sound: 'cow.mp3', color: Colors.orange, simpleImage: 'images/cow.png', realImage: 'images/dog.png'),
              ])),
              Expanded(
                  child: Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[
                FlipButton(sound: 'pig.mp3', color: Colors.green, simpleImage: 'images/pig.png', realImage: 'images/elephant.png'),
                FlipButton(sound: 'elephant.mp3', color: Colors.teal, simpleImage: 'images/elephant.png', realImage: 'images/rooster.png'),
                FlipButton(sound: 'rooster.mp3', color: Colors.blue, simpleImage: 'images/rooster.png', realImage: 'images/pig.png'),
              ])),
              Expanded(
                  child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  FlipButton(sound: 'goat.mp3', color: Colors.purple, simpleImage: 'images/goat.jpg', realImage: 'images/pig.png'),
                ],
              )),
            ],
          ),
        ),
      ),
    );
  }
}


/// You can copy this widget into separate file for better formatting
/// 
class FlipButton extends StatefulWidget {
  //declare final variables
  final String sound;
  final Color color;
  final String simpleImage;
  final String realImage;

  //constructor for this class
  const FlipButton({
    Key? key,
    required this.sound,
    required this.color,
    required this.simpleImage,
    required this.realImage,
  }) : super(key: key);

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

class _FlipButtonState extends State<FlipButton> {
  //inside the state declare variable that is about to change
  bool isReal = false;

  @override
  Widget build(BuildContext context) {
    return Expanded(
        child: FlatButton(
      onPressed: () {
        setState(() {
          isReal = !isReal;
        });
        final player = AudioCache();
        player.play(sound);
      },
      color: widget.color,
      child: isReal ? Image.asset(widget.simpleImage) : Image.asset(widget.realImage),
    ));
  }
}

Answered By – obywan

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published