Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call setState for a futurebuilder

Tags:

flutter

I want to hide floating actionbutton in the bottom of listview which is built using FutureBuilder, I've used ScrollerController inside listview to detect when user scrolls forward and hides the actionbuttons if user scrolls forwrad then called setState but whenever I call it FutureBuilder future will run repeatedly which is a async sqlite get query. I used AsynMemoizer but that didn't help too. here is my code sample:

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),
                ),
              ),
            ],
          ),
        ));
  }
}
like image 295
monajafi Avatar asked Oct 28 '25 01:10

monajafi


2 Answers

Call getDogs() in the initState, saving the Future instance in the State. Then use that Future variable in the FutureBuilder. That way it will not get called on each build if it already has been resolved.

Related with extra information: https://stackoverflow.com/a/52249579/5617722

like image 66
Martyns Avatar answered Oct 30 '25 18:10

Martyns


Thanks Martyns for a quick and smart solution,one point is that whenever I want to update listview I had to reassign a getDogs() to future state instance variable to force the FutureBuilder to run. Here is the correct source code:

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),
                ),
              ),
            ],
          ),
        ));
  }
like image 24
monajafi Avatar answered Oct 30 '25 17:10

monajafi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!