字符串列表不会在setState上更新

时间:2020-06-07 17:03:43

标签: flutter dart

我正在尝试学习Flutter,无法理解这里出了什么问题。 我有主窗口小部件(Flutter入门应用程序随附的窗口小部件),删除了主体,并添加了SearchBar小部件和PhotoList小部件,我试图从API提取数据,并更新PhotoList小部件以显示我获取的图像。

这就是我所拥有的:

class _MyHomePageState extends State<MyHomePage> {
  int _selectedSegment = 0;
  Networking _service = Networking();
  List<String> urls;

  @override
  void initState() {
    urls = [];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.only(top: 100),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                SearchBar(
                  onSearchTap: (String value) {
                    Map<String, dynamic> map;
                    List<dynamic> results;
                    dynamic urls;
                    dynamic thumbs;
                    List<String> image_urls = new List<String>();

                    _service.fetchPhotos(value).then((value) => {
                          map = jsonDecode(value.body),
                          results = map["results"],
                          thumbs = results.map((e) => e["urls"]),
                          for (dynamic thumb in thumbs)
                            {image_urls.add(thumb["thumb"].toString())},
                          this.setState(() {
                            urls = image_urls;
                          })
                        });
                  },
                ),
              ],
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  ButtonGroup(
                    titles: ["Photos", "Collections", "User"],
                    current: _selectedSegment,
                    onTab: (selected) {
                      setState(() {
                        _selectedSegment = selected;
                      });
                    },
                  ),
                ],
              ),
            ),
            PhotosGrid(urls: urls),
          ],
        ),
      ),
    );
  }
}

我在setState之后打印了URL,它确实具有可打印到控制台的值。 但是,它不会在PhotosGrid中更新,据我所知,setState应该刷新build函数,因此PhotosGrid也应该刷新,但是什么也没有发生。 这是我的PhotosGrid小部件:

class PhotosGrid extends StatefulWidget {
  List<String> urls;

  PhotosGrid({Key key, this.urls}) : super(key: key);
  @override
  _PhotosGridState createState() => _PhotosGridState();
}

class _PhotosGridState extends State<PhotosGrid> {
  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        child: ListView(
          children: <Widget>[
            for (var url in widget.urls) PhotoCard(url)
            //for (var url in widget.urls) PhotoCard(url),
          ],
        ),
      ),
    );
  }
}

这是我的照片卡:

class PhotoCard extends StatefulWidget {
  String imageUrl;

  PhotoCard({Key key, this.imageUrl}) : super(key: key);

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

class _PhotoCardState extends State<PhotoCard> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 32),
        child: Container(
          height: 220,
          decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(5.0),
              image: DecorationImage(
                image: NetworkImage(widget.imageUrl),
                fit: BoxFit.cover,
              )),
        ),
      ),
    );
  }
}

我不明白这里出了什么问题。

1 个答案:

答案 0 :(得分:0)

说实话,我发现这根本不起作用

onSearchTap: (String value) {
  Map<String, dynamic> map;
  List<dynamic> results;
  // dynamic urls; delete this line
  List<String> image_urls = <String>[];

  // does this future completes without error?
  _service.fetchPhotos(value).then((value) => {
        map = jsonDecode(value.body),
        results = map["results"],
        thumbs = results.map((e) => e["urls"]),
        for (dynamic thumb in thumbs)
          image_urls.add(thumb["thumb"].toString()),
        setState(() => urls = image_urls)
  });
},

更新

我刚刚制作了一个可复制的项目,并且没有问题地运行了

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData.light(),
        debugShowCheckedModeBanner: false,
        home: MyHomePage());
  }
}

Future<List<String>> thumbnails(int value) {
  String url = 'https://picsum.photos/id/x/200/300'; //dummy api to retrieve pics
  List<String> myThumbs = [];
  int n = value ?? 0;

  for(int i = 0; i < n; i++) myThumbs.add(
    url.replaceFirst('x', i.toString()) //just change x for any number to retrieve a real url from the api
  );

  myThumbs.shuffle();

  return Future<List<String>>.value(myThumbs);
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> urls;

  @override
  void initState() {
    urls = <String>[];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.only(top: 100),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Padding(
             padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
             child: TextField(
              key: Key('MyTextField'),
              onSubmitted: (String value) {
                thumbnails(int.tryParse(value)).then((list) {
                  setState(() => urls = list);
                });
              },
             ),
            ),
            PhotosGrid(urls: urls),
          ],
        ),
      ),
    );
  }
}

class PhotosGrid extends StatefulWidget {
  final List<String> urls;

  PhotosGrid({Key key, this.urls}) : super(key: key);
  @override
  _PhotosGridState createState() => _PhotosGridState();
}

class _PhotosGridState extends State<PhotosGrid> {
  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        child: ListView(
          children: <Widget>[
            for (var url in widget.urls) PhotoCard(imageUrl: url)
          ],
        ),
      ),
    );
  }
}

class PhotoCard extends StatefulWidget {
  final String imageUrl;

  PhotoCard({Key key, this.imageUrl}) : super(key: key);

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

class _PhotoCardState extends State<PhotoCard> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 32),
        child: Container(
          height: 220,
          decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(5.0),
              image: DecorationImage(
                image: NetworkImage(widget.imageUrl),
                fit: BoxFit.cover,
              )),
        ),
      ),
    );
  }
}

我只是放置了一个TextField而不是您的SearchBar(因为我没有SearchBar类,但是它应该可以正常工作)。提交后,我尝试将其解析为一个数字(请插入数字),然后从图片的虚拟api创建一个列表(我没有您正在使用的服务)。想法是一样的,我仍然使用您的所有类并毫无问题地运行它

Example

我唯一的建议是尝试尽量减少变量的数量(或仅在需要时创建它们),如果已经知道期望值的类型,则避免使用动态变量

onSearchTap: (String value) {

   _service.fetchPhotos(value).then((value) => {
    //declare them here if you only will use them inside the then future
     Map<String, dynamic> map = jsonDecode(value.body),
     List<dynamic> results = map["results"],
     var thumbs = results.map((e) => e["urls"]),
     List<String> image_urls = <String>[];
     for (var thumb in thumbs) image_urls.add(thumb["thumb"].toString()),
     setState(() => urls = image_urls);
  });
 },