【问题标题】:Flutter: save list as favoriteFlutter:将列表保存为收藏夹
【发布时间】:2020-10-24 12:03:48
【问题描述】:

我的 JSON 数据文件看起来像这样,您可以看到“景点”本身就是每个城市下的列表:

[
    {
        "city": "text text text",
        "attractions": [
            "text text ",
            "text text"
        ],
    },
    {
        "city": "text text text",
        "attractions": [
            "text text",
            "text text",
        ],
    },
]

以下代码基本上是一个从上述 JSON 获取数据的列表视图构建器。现在,当单击该城市时,它会导航到下一页,其中会显示该城市的景点列表。正如您在代码中看到的那样,有一个选项可以将另一个页面中的城市保存为收藏夹,该收藏夹可以显示在另一个收藏夹列表中:

class Index extends StatefulWidget {
  @override
  _IndexState createState() => _IndexState();
}
List data;
List<Cities> citylist = List();
List<Cities> citysavedlist = List();
int index;
class _IndexState extends State<Index> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: listView(),
    );
  }
  Future<String> fetchData() async {
    String data =
        await DefaultAssetBundle.of(context).loadString("assets/data.json");
    final jsonResult = json.decode(data);
    this.setState(() {
      jsonResult
          .forEach((element) => citylist.add(new Cities.fromJson(element)));
    });
    return "Success!";
  }

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

  listView() {
    return ListView.builder(
      itemCount: citylist == null ? 0 : citylist.length,
      itemBuilder: (context, index) {
        return Column(
          children: <Widget>[_buildRow(index, citylist)],
        );
      },
    );
  }

  Widget _buildRow(index, citylist) {
    final bool alreadySaved = citysavedlist.contains(citylist[index]);
    return Padding(
      padding: const EdgeInsets.only(top: 5.0, left: 5.0, right: 5.0),
      child: Card(
        child: ListTile(
            title:
                Text(citylist[index].title, style: TextStyle(fontSize: 22.0)),
            trailing: IconButton(
              icon: Icon(
                alreadySaved ? Icons.star : Icons.star_border,
                color: alreadySaved ? Colors.blue : Colors.blue,
              ),
              onPressed: () {
                setState(() {
                  if (alreadySaved) {
                    citysavedlist.remove(citylist[index]);
                  } else {
                    citysavedlist.add(citylist[index]);
                  }
                });
              },
            ), //subtitle: Text(subtitle),
            onTap: () {
              Navigator.push(
                  context,
                  MaterialPageRoute(
                      builder: (context) => Detail(citylist[index])));
            }),
      ),
    );
  }


void _pushSaved() {
    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (BuildContext context) {
          final Iterable<ListTile> tiles = citysavedlist.map(
            (Cities pair) {
              return ListTile(
                  title: Text(
                    pair.city,
                  ),
                  onTap: () {
                    Navigator.push(context,
                        MaterialPageRoute(builder: (context) => Detail(pair)));
                  });
            },
          );

          final List<Widget> divided = ListTile.divideTiles(
            context: context,
            tiles: tiles,
          ).toList();
          return Scaffold(
            appBar: AppBar(
              title: const Text('Saved Suggestions'),
            ),
            body: ListView(children: divided),
          );
        },
      ),
    );
  }
}

这是模型类:

List<Cities> citiesFromJson(String str) =>
    List<Cities>.from(json.decode(str).map((x) => Cities.fromJson(x)));
String citiesToJson(List<Cities> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Cities {
  Cities({
    this.city,
    this.attractions,
  });

  String city;
  List<String> attractions;

  factory Cities.fromJson(Map<String, dynamic> json) => Cities(
        city: json["city"],
        attractions: List<String>.from(json["attractions"].map((x) => x)),
      );

  Map<String, dynamic> toJson() => {
        "city": city,
        "attractions": List<dynamic>.from(attractions.map((x) => x)),
      };
}

下面的代码是我需要你帮助的地方,它是用于详细页面,当有人点击一个城市时,这个页面会以列表的形式显示每个城市的景点。在此页面中,我希望能够将景点保存为收藏夹,以便在其他页面中显示。

class Detail extends StatefulWidget {
  final Cities cities;
  Detail(this.cities);
  @override
  _DetailState createState() => _DetailState();
}
class _DetailState extends State<Detail> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.cities.city),
      ),
      body: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text(
                widget.cities.city,
                style: TextStyle(fontSize: 20),
              ),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: widget.cities.attractions.length,
                itemBuilder: (BuildContext context, int index) {
                  return Card(
                      child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text(widget.cities.attractions[index]),
                  ));
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

我需要您的帮助,请添加此功能“将景点保存为收藏”。我需要在一个“已保存的景点页面”中显示所有已保存的景点,无论它属于哪个城市。

有人可以帮忙吗

【问题讨论】:

    标签: flutter dart flutter-listview


    【解决方案1】:

    查看我制作的示例。 以下是我添加的 json,我只是将数据更改为您的理解

    [
       {
           "city": "Canada",
           "attractions": [
               "Niagara Falls: An Elegant View",
               "Whistler: Your Perfect Ski Resort"
           ]
       },
       {
           "city": "Germany",
           "attractions": [
               "Berlin",
               "Munich"
           ]
       }
    ]
    
    

    您提供的 json 中的模型类:

    // To parse this JSON data, do
    //
    //     final cities = citiesFromJson(jsonString);
    
    import 'dart:convert';
    
    List<Cities> citiesFromJson(String str) => List<Cities>.from(json.decode(str).map((x) => Cities.fromJson(x)));
    
    String citiesToJson(List<Cities> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
    
    class Cities {
        Cities({
            this.city,
            this.attractions,
        });
    
        String city;
        List<String> attractions;
    
        factory Cities.fromJson(Map<String, dynamic> json) => Cities(
            city: json["city"],
            attractions: List<String>.from(json["attractions"].map((x) => x)),
        );
    
        Map<String, dynamic> toJson() => {
            "city": city,
            "attractions": List<dynamic>.from(attractions.map((x) => x)),
        };
    }
    
    

    这是主要的 ui 和 miplementation

    import 'dart:convert';
    
    import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Index(),
        );
      }
    }
    
    class Index extends StatefulWidget {
      @override
      _IndexState createState() => _IndexState();
    }
    
    List data;
    List<Cities> citylist = List();
    List<Cities> citysavedlist = List();
    int index;
    
    class _IndexState extends State<Index> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: listView(),
        );
      }
    
      Future<String> fetchData() async {
        String data =
            await DefaultAssetBundle.of(context).loadString("json/parse.json");
        final jsonResult = json.decode(data);
        this.setState(() {
          jsonResult
              .forEach((element) => citylist.add(new Cities.fromJson(element)));
        });
        return "Success!";
      }
    
      @override
      void initState() {
        super.initState();
        fetchData();
      }
    
      listView() {
        return ListView.builder(
          itemCount: citylist == null ? 0 : citylist.length,
          itemBuilder: (context, index) {
            return Column(
              children: <Widget>[_buildRow(index, citylist)],
            );
          },
        );
      }
    
      Widget _buildRow(index, citylist) {
        final bool alreadySaved = citysavedlist.contains(citylist[index]);
        return Padding(
          padding: const EdgeInsets.only(top: 5.0, left: 5.0, right: 5.0),
          child: Card(
            child: ListTile(
                title: Text(citylist[index].city, style: TextStyle(fontSize: 22.0)),
                trailing: IconButton(
                  icon: Icon(
                    alreadySaved ? Icons.star : Icons.star_border,
                    color: alreadySaved ? Colors.blue : Colors.blue,
                  ),
                  onPressed: () {
                    setState(() {
                      if (alreadySaved) {
                        citysavedlist.remove(citylist[index]);
                      } else {
                        citysavedlist.add(citylist[index]);
                      }
                    });
                  },
                ), //subtitle: Text(subtitle),
                onTap: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => Detail(citylist[index])));
                }),
          ),
        );
      }
    
      void _pushSaved() {
        Navigator.of(context).push(
          MaterialPageRoute<void>(
            builder: (BuildContext context) {
              final Iterable<ListTile> tiles = citysavedlist.map(
                (Cities pair) {
                  return ListTile(
                      title: Text(
                        pair.city,
                      ),
                      onTap: () {
                        Navigator.push(context,
                            MaterialPageRoute(builder: (context) => Detail(pair)));
                      });
                },
              );
    
              final List<Widget> divided = ListTile.divideTiles(
                context: context,
                tiles: tiles,
              ).toList();
              return Scaffold(
                appBar: AppBar(
                  title: const Text('Saved Suggestions'),
                ),
                body: ListView(children: divided),
              );
            },
          ),
        );
      }
    }
    
    class Detail extends StatefulWidget {
      final Cities cities;
      Detail(this.cities);
      @override
      _DetailState createState() => _DetailState();
    }
    
    class _DetailState extends State<Detail> {
      List<String> attractionsSavedList = List();
    
      @override
      void initState() {
        super.initState();
    
        getData();
      }
    
      /* 
    
      This is done using the shared Prefrences where the saving structure is String
    
      This is where key is the unique user id, this is because if you logout with another user then this will differentiate the user and get the string based on it. 
    
      I have saved  a Map<String,List<String>> as String encoded it and while using it decode the string which will give you the map.
       */
    
      getData() async {
        SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
        var stringvalue = sharedPreferences.getString("Id");
        print(stringvalue);
        if (stringvalue != null) {
          Map<String, dynamic> newMap = json.decode(stringvalue);
          print('This is the city selected : ${newMap[widget.cities.city]}');
    
          var newlist = newMap[widget.cities.city];
          if (newlist != null) {
            newlist.forEach((element) {
              print(element);
              attractionsSavedList.add(element);
            });
          }
        } else {
          print('No values to show');
        }
    
        setState(() {});
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.cities.city),
          ),
          body: Container(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.all(15.0),
                      child: GestureDetector(
                        onTap: () {
                          Navigator.push(
                              context,
                              MaterialPageRoute(
                                  builder: (context) => SavedAttractions()));
                        },
                        child: Text(
                          'Saved Attractions',
                          style: TextStyle(
                            fontSize: 18,
                            decoration: TextDecoration.underline,
                          ),
                        ),
                      ),
                    )
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    widget.cities.city,
                    style: TextStyle(fontSize: 20),
                  ),
                ),
                Expanded(
                  child: ListView.builder(
                    itemCount: widget.cities.attractions.length,
                    itemBuilder: (BuildContext context, int index) {
                      final bool attractionsExists = attractionsSavedList
                          .contains(widget.cities.attractions[index]);
                      return Card(
                          child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: <Widget>[
                            Text(widget.cities.attractions[index]),
                            IconButton(
                              onPressed: () async {
                                SharedPreferences _prefs =
                                    await SharedPreferences.getInstance();
    
                                setState(() {
                                  if (attractionsExists) {
                                    attractionsSavedList
                                        .remove(widget.cities.attractions[index]);
    
                                    String uniqueid = _prefs.getString("Id");
    
                                    Map<String, dynamic> mapvalue =
                                        json.decode(uniqueid);
    
                                    List list = mapvalue[widget.cities.city];
    
                                    String stringtoRemove;
    
                                    list.forEach((element) {
                                      if (element ==
                                          widget.cities.attractions[index]) {
                                        stringtoRemove =
                                            widget.cities.attractions[index];
                                      }
                                    });
    
                                    if (attractionsSavedList.length == 0) {
                                      list.remove(stringtoRemove);
    
                                      if (list.length == 0) {
                                        mapvalue.remove(stringtoRemove);
                                        _prefs.setString(
                                            "Id", json.encode(mapvalue));
                                      }
                                    } else {
                                      list.remove(stringtoRemove);
                                      mapvalue.remove(stringtoRemove);
                                      
                                      mapvalue[widget.cities.city] = list;
    
                                      _prefs.setString("Id", json.encode(mapvalue));
                                    }
                                  } else {
                                    print(
                                        'It does not exist in the list ${attractionsSavedList.length}');
                                    attractionsSavedList
                                        .add(widget.cities.attractions[index]);
    
                                    var uniqueId = _prefs.getString("Id");
                                    if (uniqueId != null) {
                                      Map<String, dynamic> newMap =
                                          json.decode(uniqueId);
                                      if (attractionsSavedList.length > 0) {
                                        print(widget.cities.city);
    
                                        newMap[widget.cities.city] =
                                            attractionsSavedList;
                                        _prefs.setString("Id", json.encode(newMap));
                                      }
                                    } else {
                                      if (attractionsSavedList.length > 0) {
                                        Map newMap = Map();
                                        newMap[widget.cities.city] =
                                            attractionsSavedList;
                                        _prefs.setString("Id", json.encode(newMap));
                                      }
                                    }
    
                                    /*  {
                                      widget.cities.city: attractionsSavedList
                                    }; */
    
                                    attractionsSavedList.forEach((element) {
                                      print(
                                          'This is the list after adding  $element');
                                    });
                                  }
                                });
                              },
                              icon: Icon(
                                attractionsExists ? Icons.star : Icons.star_border,
                                color:
                                    attractionsExists ? Colors.blue : Colors.blue,
                              ),
                            )
                          ],
                        ),
                      ));
                    },
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class SavedAttractions extends StatefulWidget {
      @override
      _SavedAttractionsState createState() => _SavedAttractionsState();
    }
    
    class _SavedAttractionsState extends State<SavedAttractions> {
      List<String> attractionsList = List();
      bool _isLoading = false;
    
      @override
      void initState() {
        super.initState();
        getSavedAttractions();
      }
    
      getSavedAttractions() async {
        setState(() {
          _isLoading = true;
        });
        SharedPreferences preferences = await SharedPreferences.getInstance();
    
        String value = preferences.getString('Id');
    
        Map<String, dynamic> newMapVAlue = json.decode(value);
    
        newMapVAlue.forEach((key, value) {
          value.forEach((elements) {
            attractionsList.add(elements);
          });
        });
    
        setState(() {
          _isLoading = false;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: _isLoading
              ? Center(
                  child: CircularProgressIndicator(),
                )
              : ListView.builder(
                  itemCount: attractionsList.length,
                  shrinkWrap: true,
                  itemBuilder: (context, index) {
                    return Card(
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Text(attractionsList[index]),
                      ),
                    );
                  }),
        );
      }
    }
    
    List<Cities> citiesFromJson(String str) =>
        List<Cities>.from(json.decode(str).map((x) => Cities.fromJson(x)));
    String citiesToJson(List<Cities> data) =>
        json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
    
    class Cities {
      Cities({
        this.city,
        this.attractions,
      });
    
      String city;
      List<String> attractions;
    
      factory Cities.fromJson(Map<String, dynamic> json) => Cities(
            city: json["city"],
            attractions: List<String>.from(json["attractions"].map((x) => x)),
          );
    
      Map<String, dynamic> toJson() => {
            "city": city,
            "attractions": List<dynamic>.from(attractions.map((x) => x)),
          };
    }
    
    

    【讨论】:

    • 感谢您的回复。不,我不想要通过 api,它应该是本地的,就像我已经拥有的城市一样。
    • 看看这个例子,让我知道它是否有效。
    • 非常感谢 Sagar 的帮助和支持。我能请你帮个忙吗。您能否将共享偏好添加到第一个列表(城市列表)。
    • 希望我如何实现它
    • 我想你实现它就像你为景点做的那样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2021-11-20
    • 1970-01-01
    • 2013-10-20
    • 2021-11-18
    • 1970-01-01
    相关资源
    最近更新 更多