如何为FutureBuilder调用setState

时间:2019-06-17 11:41:29

标签: flutter

我想在使用FutureBuilder构建的listview的底部隐藏浮动的actionbutton,我在listview中使用了ScrollerController来检测用户何时向前滚动,并且如果用户滚动前叉然后调用setState则隐藏actionbutton,但是无论何时我调用FutureBuilder future将重复运行,这是一个异步sqlite get查询。我使用了AsynMemoizer,但这也无济于事。这是我的代码示例:

class _MyHomePageState extends State<MyHomePage> {
  bool show = true;
  ScrollController _controller = ScrollController();
  final AsyncMemoizer _memoizer = AsyncMemoizer();

  @override
  void initState() {
    super.initState();
    _controller.addListener(listener);
  }

  void listener() {
    if (_controller.position.userScrollDirection == ScrollDirection.forward) {
      show = true;
    } else if (_controller.position.userScrollDirection ==
        ScrollDirection.reverse) {
      show = false;
    }
    setState(() {});
  }

  int _counter = 0;
  int id = 0;
  Future<List<Dog>> _getDogs() async {
    // return this._memoizer.(() async {
    //   return await getDogs();
    // });
  }

  void _insertDog() async {
    id++;
    var fido = Dog(id: id, name: "fido$id", age: id * 3);
    await insertDog(fido);
    setState(() {});
  }

  void _updateDog(Dog fido) async {
    fido = Dog(
      id: fido.id,
      name: fido.name,
      age: fido.age + 7,
    );
    await updateDog(fido);
    setState(() {});
  }

  void _deleteDog(int id) async {
    await deleteDog(id);
    setState(() {});
  }

  @override
  void dispose() {
    _controller.removeListener(listener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Sqflite Demo"),
        ),
        body: Container(
          child: FutureBuilder(
            future: getDogs(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return ListView.builder(
                  controller: _controller,
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      title: Text(snapshot.data[index].name),
                      subtitle: Text("Age: ${snapshot.data[index].age}"),
                      onTap: () => _updateDog(snapshot.data[index]),
                      onLongPress: () => _deleteDog(snapshot.data[index].id),
                    );
                  },
                );
              } else {
                return Container(
                    child: Center(
                  child: Text("Loading..."),
                ));
              }
            },
          ),
        ),
        floatingActionButton: Visibility(
          visible: show,
          child: Stack(
            children: <Widget>[
              Padding(
                padding: EdgeInsets.only(left: 30),
                child: Align(
                  alignment: Alignment.bottomLeft,
                  child: FloatingActionButton(
                    onPressed: () {
                      setState(() {});
                    },
                    child: Icon(Icons.refresh),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.bottomRight,
                child: FloatingActionButton(
                  onPressed: _insertDog,
                  tooltip: 'Increment',
                  child: Icon(Icons.add),
                ),
              ),
            ],
          ),
        ));
  }
}

2 个答案:

答案 0 :(得分:0)

在initState中调用getDogs(),将Future实例保存在State中。然后在FutureBuilder中使用该Future变量。这样,如果已经解决,它将不会在每个构建中得到调用。

答案 1 :(得分:0)

感谢Martyns提供一种快速,智能的解决方案,一点是,每当我要更新listview时,都必须将getDogs()重新分配给将来的状态实例变量,以强制FutureBuilder运行。这是正确的源代码:

class _MyHomePageState extends State<MyHomePage> {
  bool show = true;
  ScrollController _controller = ScrollController();
  Future<List<Dog>> future;
  @override
  void initState() {
    super.initState();
    future = getDogs();
    _controller.addListener(listener);
  }

  void listener() {
    if (_controller.position.userScrollDirection == ScrollDirection.forward) {
      show = true;
    } else if (_controller.position.userScrollDirection ==
        ScrollDirection.reverse) {
      show = false;
    }
    setState(() {});
  }

  int _counter = 0;
  int id = 0;
  void _insertDog() async {
    id++;
    var fido = Dog(id: id, name: "fido$id", age: id * 3);
    await insertDog(fido);
    setState(() {future = getDogs();});
  }

  void _updateDog(Dog fido) async {
    fido = Dog(
      id: fido.id,
      name: fido.name,
      age: fido.age + 7,
    );
    await updateDog(fido);
    setState(() {future = getDogs();});
  }

  void _deleteDog(int id) async {
    await deleteDog(id);
    setState(() {future = getDogs();});
  }

  @override
  void dispose() {
    _controller.removeListener(listener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Sqflite Demo"),
        ),
        body: Container(
          child: FutureBuilder(
            future: future,
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.connectionState == ConnectionState.done) {
                return ListView.builder(
                  controller: _controller,
                  itemCount: snapshot.data.length,
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      title: Text(snapshot.data[index].name),
                      subtitle: Text("Age: ${snapshot.data[index].age}"),
                      onTap: () => _updateDog(snapshot.data[index]),
                      onLongPress: () => _deleteDog(snapshot.data[index].id),
                    );
                  },
                );
              } else {
                return Container(
                    child: Center(
                  child: Text("Loading..."),
                ));
              }
            },
          ),
        ),
        floatingActionButton: Visibility(
          visible: show,
          child: Stack(
            children: <Widget>[
              Padding(
                padding: EdgeInsets.only(left: 30),
                child: Align(
                  alignment: Alignment.bottomLeft,
                  child: FloatingActionButton(
                    onPressed: () {
                      setState(() {future = getDogs();});
                    },
                    child: Icon(Icons.refresh),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.bottomRight,
                child: FloatingActionButton(
                  onPressed: _insertDog,
                  tooltip: 'Increment',
                  child: Icon(Icons.add),
                ),
              ),
            ],
          ),
        ));
  }