-1

I am using a CustomScrollView and trying to add a like button in the SliverAppBar actions which by tapping on it, its shape changes. I am making the changes inside setState. But despite that the build method is being called, UI is not being updated.

TextButton(
  onPressed: () {
     bool added = courseStore.addToUserFavoriteCourses(widget.courseDetails);
     added ?
       setState(() {
         iconData = Icons.favorite;
       }) :
       setState(() {
         iconData = Icons.favorite_border;
       });
  },
  child: Icon(
     iconData,
     size: 20,
     color: Colors.white,
  ),
)
DonAlex1897
  • 65
  • 1
  • 8
  • Can you print(added). I'm windering if you toggle added with courseStore.addToUserFavoriteCourses? Also, what icon do you see, Icons.favorite or favorite_border? – YoBo Feb 19 '21 at 10:15
  • I would like to see the entire code so that, I can understand what exactly is going wrong. Because the current code shared by you doesn't have any problems. – Preet Shah Feb 19 '21 at 10:28
  • @YoBo each time the icon is tapped by the user, the value of 'added' should be changed. And it is happening as expected. It means that if I tap the icon, the first setState will be applied and the next time I press it, the second setState. – DonAlex1897 Feb 19 '21 at 12:42

1 Answers1

1

The above code is not saving state of iconData yet.

The state of like/unlike needs to be moved up into a parent widget scope. Its value can then be referenced below in children like TextButton.

When TextButton is rebuilt (changing from unlike to like & back) it can use the state that persists above it.

Notice below how iconData lives in the State widget. This widget designed to hold state for its children.

You can copy paste this code into a Flutter/Dart file to test:

import 'package:flutter/material.dart';

// ↓ Use this as your button
class StatefulButton extends StatefulWidget {
  final double size;
  final Color color;

  StatefulButton({this.size = 20, this.color = Colors.black});

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

class _StatefulButtonState extends State<StatefulButton> {
  IconData iconData = Icons.favorite_border;
  // ↑ Move your state up to here

  @override
  Widget build(BuildContext context) {
    return Center(
      child: TextButton(
        child: Icon(
          iconData,
          size: widget.size,
          color: widget.color,
        ),
        onPressed: toggleLike,
      ),
    );
  }

  void toggleLike() {
    setState(() {
      iconData = iconData == Icons.favorite ? Icons.favorite_border : Icons.favorite;
    });
  }
}


/// Just a container page for the example above
class TextButtonStatePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TextButtonState'),
      ),
      body: StatefulButton()
    );
  }
}

Here's an example of using the StatefulButton above inside a CustomScrollView. The CustomScrollView code I stole straight from the Flutter docs website. The only edit I've made is to add the StatefulButton above into each sliver, to show it changing its state when clicked.

/// Flutter code sample for CustomScrollView

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

/// This is the stateful widget that the main application instantiates.
class CustomScrollviewPage extends StatefulWidget {
  CustomScrollviewPage({Key key}) : super(key: key);

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

/// This is the private State class that goes with MyStatefulWidget.
class _CustomScrollviewPageState extends State<CustomScrollviewPage> {
  List<int> top = [];
  List<int> bottom = [0];

  @override
  Widget build(BuildContext context) {
    const Key centerKey = ValueKey('bottom-sliver-list');
    return Scaffold(
      appBar: AppBar(
        title: const Text('Press on the plus to add items above and below'),
        leading: IconButton(
          icon: const Icon(Icons.add),
          onPressed: () {
            setState(() {
              top.add(-top.length - 1);
              bottom.add(bottom.length);
            });
          },
        ),
      ),
      body: CustomScrollView(
        center: centerKey,
        slivers: <Widget>[
          SliverList(
            delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.blue[200 + top[index] % 4 * 100],
                  height: 100 + top[index] % 4 * 20.0,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                    Text('Item: ${top[index]}'),
                    StatefulButton() // ← STATEFUL BUTTON HERE
                  ],),
                );
              },
              childCount: top.length,
            ),
          ),
          SliverList(
            key: centerKey,
            delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.blue[200 + bottom[index] % 4 * 100],
                  height: 100 + bottom[index] % 4 * 20.0,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text('Item: ${bottom[index]}'),
                      StatefulButton()  // ← STATEFUL BUTTON HERE
                    ],
                  ),
                );
              },
              childCount: bottom.length,
            ),
          ),
        ],
      ),
    );
  }
}

Baker
  • 24,730
  • 11
  • 100
  • 106
  • Thanks for your answer. I am doing what you did in the example. But it does not work. I think maybe setState is not applied on CustomScrollView. – DonAlex1897 Feb 24 '21 at 21:07
  • CustomScrollView itself is a `StatelessWidget`, so it itself doesn't have a setState method. But this shouldn't affect how widgets within the scroll view can maintain their state. I've taken an example from the flutter documentation website for CustomScrollView and added the StatefulButton to each sliver. Hopefully this helps. – Baker Feb 26 '21 at 04:28