0

I'm new to flutter and I'm following flutter app intro part 2 and wanted to add some additional functionality. At the current state of the part 2, if you click on the word on the screen it highlights the heart icon to red and adds the word pair into the list and updates the list showing in the saved words list.

Front page

I wanted to add in a removing function to the new saved word page and updates the original page to be not selected with a heart symbol.

Saved list page with delete icon

The part 2 example creates a new page with MertrialPageRoute on the fly (I'm assuming a stateless page) which I could modify as:

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

void main() => runApp(MyApp());
final _saved = <WordPair>{};

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();
    return MaterialApp(
      title: 'Startup Name Generator',
      theme: ThemeData(
        primaryColor: Colors.white,
      ),
      home: RandomWords(),
    );
  }
}

class RandomWords extends StatefulWidget {
  const RandomWords({Key? key}) : super(key: key);

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

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18);
  final _saved = <WordPair>{};

  void _pushSaved() {
    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (BuildContext context){
          final tiles = _saved.map(
            (WordPair pair) {
              final canRemove = _saved.contains(pair);
              return ListTile(
                title: Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
                trailing: Icon(
                  Icons.cancel_outlined,
                  color: canRemove ? Colors.black : null,
                ),
                onTap: () {
                  setState(() {
                    if (canRemove) {
                      _saved.remove(pair);
                    }
                  });
                },
              );
            },
          );

          final divided = tiles.isNotEmpty
            ? ListTile.divideTiles(context: context, tiles: tiles).toList()
            : <Widget>[];

          return Scaffold(
            appBar: AppBar(
              title: Text('Saved Suggestions'),
            ),
            body: ListView(children: divided),
          );
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Startup Name Generator'),
        actions: [
          IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
    
        ]
      ),
      body: _buildSuggestions(),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: /*1*/ (context, i) {
          if (i.isOdd) return const Divider();
          /*2*/

          final index = i ~/ 2; /*3*/
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10)); /*4*/
          }
          return _buildRow(_suggestions[index]);
        });
  }

  Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }
}

But this only modifies the list in the background and doesn't update the UI.

I looked into flutter delete item from listview and Passing Data to a Stateful Widget and Working with Callback In Flutter. So far, I've modified it again to change the MaterialPageRoute to create a new stateful widget and removing the void _pushSaved() function while making final _saved = <WordPair>{}; a global as suggested:

// add a new stateful page
class DetailPage extends StatefulWidget {
  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  final TextStyle _biggerFont = const TextStyle(fontSize: 18.0);

  @override
  Widget build(BuildContext context) {

    Iterable<ListTile> tiles = _saved.map((WordPair pair) {
      final canRemove = _saved.contains(pair);
      return new ListTile(
        trailing: Icon(
          Icons.cancel_outlined,
          color:  Colors.black,
        ),
        onTap: () {
          setState(() {
            _saved.remove(pair);
          });
        },
        title: new Text(
          pair.asPascalCase,
          style: _biggerFont,
        ),
      );
    });

    final List<Widget> divided = ListTile.divideTiles(
      context: context,
      tiles: tiles,
    ).toList();

    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Saved Suggestions'),
      ),
      body: new ListView(children: divided),
    );
  }
}

I've also changed the Wdiget build in the class _RandomWordsState into:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Startup Name Generator'),
        actions: [
          //IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
          IconButton(icon: Icon(Icons.list), onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => DetailPage()
              )
            );
          }),
        ]
      ),
      body: _buildSuggestions(),
    );
  }

so it calls the new stateful page instead. This did update the UI now when I press the X button in the saved page and removed the wordpair. Although, when I went back to the original page, the heart symbol was still highlighted on the removed wordpair. I realized that even if I remove the word I still need to somehow update the original page.

I figured callback could solve this but I'm not too sure how to implement that. Also, it could be done by somehow sharing the data back into the original page as well or updating selected pages when a certain action happens (e.g. update original page and saved page only when a list gets updated).

Any suggestions is appreciated!

koop
  • 1
  • 1

0 Answers0