'how to change _Visibility of FloatingActionButton for each ListView item individually?

This question is a follow up to a previous post i made:

How to apply Scroll controller for multiple SilverList or ListView items?

In that post the error was resolved but the example i showed was not correct.

There is also a problem when scrolling horizontally on each ListView() items. When you scroll on one item then the _Visibiltiy of all the FloatingActionButtons of the other items are also affected. enter image description here

My question is how can i only affect the _Visibility of one individual FloatingActionButtons() in ListView() be affected when scrolling on a individual CustomScrollView() generated inside ListView() and not affect the _Visibility of the other FloatingActionButton()?

I also might add that the snapshot.data.length varies from the data i generate from the json. So there needs to be solution where i don't alter the data itself but rather the _Visibility should change inidividually based on json data.

This is all the code i use:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ThemeData? themeData;
  const MyApp({Key? key, this.themeData}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: AppView2(),
    );
  }
}

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

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

class _AppViewState extends State<AppView2> {
  late ScrollController _hideButtonController;
  var _isVisible;

  Future<void> generateList() async {
    String responseBody =
    await rootBundle.loadString("assets/tableJsonData.json");
    var list = await json.decode(responseBody);
    return list;
  }

  @override
  void initState() {
    generateList();
    _isVisible = true;
    _hideButtonController = ScrollController();
    _hideButtonController.addListener(() {
      if (_hideButtonController.positions
          .any((pos) => pos.userScrollDirection == ScrollDirection.reverse)) {
        if (_isVisible == true) {
          setState(() {
            _isVisible = false;
          });
        } else if (_isVisible == false) {
          Future.delayed(const Duration(milliseconds: 1000), () {
            setState(() {
              _isVisible = true;
            });
          });
        }
      } else {
        if (_hideButtonController.positions
            .any((pos) => pos.userScrollDirection == ScrollDirection.forward)) {
          if (_isVisible == true) {
            setState(() {
              _isVisible = false;
            });
          } else if (_isVisible == false) {
            Future.delayed(const Duration(milliseconds: 1000), () {
              setState(() {
                _isVisible = true;
              });
            });
          }
        }
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            appBar: AppBar(
              title: Text('DataTable'),
            ),
            body: FutureBuilder(
              future: generateList(),
              builder: (context, AsyncSnapshot snapShot) {
                if (snapShot.hasData) {
                  return ListView.builder(
                      shrinkWrap: true,
                      scrollDirection: Axis.vertical,
                      itemCount: snapShot.data.length,
                      itemBuilder: (BuildContext context, int index) {
                        return SizedBox(
                          height: 250,
                          child: Stack(
                            children: [
                              CustomScrollView(
                                controller: _hideButtonController,
                                scrollDirection: Axis.horizontal,
                                shrinkWrap: true,
                                slivers: <Widget>[
                                  SliverPadding(
                                    padding: const EdgeInsets.all(20.0),
                                    sliver: SliverList(
                                      delegate: SliverChildListDelegate(
                                        <Widget>[
                                          Center(
                                            child: Text(snapShot.data[index]
                                            ["long-text"]
                                                .toString()),
                                          )
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                              Visibility(
                                visible: _isVisible,
                                child: Align(
                                  alignment: Alignment.centerRight,
                                  child: FloatingActionButton(
                                      child:
                                      const Icon(Icons.arrow_forward_ios),
                                      onPressed: () {}),
                                ),
                              )
                            ],
                          ),
                        );
                      });
                } else {
                  return CircularProgressIndicator();
                }
              },
            )));
  }
}

This is the json file i read from :

[
  {
    "long-text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  },
  {
    "long-text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  },
  {
    "long-text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  },
  {
    "long-text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  },
  {
    "long-text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  }

]

Edit

So i followed the answer of @SlowDeepCoder and edited my original code and i also added a controller but i when i add the condition in the ListView i cannot init state in the app and i get error: type 'Null' is not a subtype of type 'bool' This is all the code that is edited :

import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:test_project/controllers/swipe_controller.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ThemeData? themeData;
  const MyApp({Key? key, this.themeData}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: AppView2(),
    );
  }
}

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

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

class _AppViewState extends State<AppView2> {
  late ScrollController _hideButtonController;

  var isVisible = true;
  Future<void> generateList() async {
    String responseBody =
        await rootBundle.loadString("assets/tableJsonData.json");
    var list = await json.decode(responseBody);
    return list;
  }

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            appBar: AppBar(
              title: Text('DataTable'),
            ),
            body: FutureBuilder(
              future: generateList(),
              builder: (context, AsyncSnapshot snapShot) {
                if (snapShot.hasData) {
                  return ListView.builder(
                      shrinkWrap: true,
                      scrollDirection: Axis.vertical,
                      itemCount: snapShot.data.length,
                      itemBuilder: (BuildContext context, int index) {
                        isVisible = SwipeController().swipeHandler(isVisible);
                        return SizedBox(
                          height: 250,
                          child: Stack(
                            children: [
                              CustomScrollView(
                                controller: _hideButtonController,
                                scrollDirection: Axis.horizontal,
                                shrinkWrap: true,
                                slivers: <Widget>[
                                  SliverPadding(
                                    padding: const EdgeInsets.all(20.0),
                                    sliver: SliverList(
                                      delegate: SliverChildListDelegate(
                                        <Widget>[
                                          Center(
                                            child: Text(snapShot.data[index]
                                                    ["long-text"]
                                                .toString()),
                                          )
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                              Visibility(
                                visible: isVisible,
                                child: Align(
                                  alignment: Alignment.centerRight,
                                  child: FloatingActionButton(
                                      child:
                                          const Icon(Icons.arrow_forward_ios),
                                      onPressed: () {}),
                                ),
                              )
                            ],
                          ),
                        );
                      });
                } else {
                  return CircularProgressIndicator();
                }
              },
            )));
  }
}
class SwipeController {
  swipeHandler(isVisible) {
    var _hideButtonController = ScrollController();
    _hideButtonController.addListener(() {
      if (_hideButtonController.position.userScrollDirection ==
          ScrollDirection.reverse) {
        if (isVisible == true) {
          isVisible = false;
        }
      } else {
        if (_hideButtonController.position.userScrollDirection ==
            ScrollDirection.forward) {
          if (isVisible == false) {
            isVisible = true;
          }
        }
      }
    });
  }
}

I still don't know how i can resolve the issue



Solution 1:[1]

You can create a local variable in your ListView's itemBuilder to describe the visibility of the FloatingActionButton for each item. For example:

import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ThemeData? themeData;
  const MyApp({Key? key, this.themeData}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: AppView2(),
    );
  }
}

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

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

class _AppViewState extends State<AppView2> {
  late ScrollController _hideButtonController;

  Future<void> generateList() async {
    String responseBody =
    await rootBundle.loadString("assets/tableJsonData.json");
    var list = await json.decode(responseBody);
    return list;
  }

  @override
  void initState() {
    _hideButtonController = ScrollController();
    _hideButtonController.addListener(() {
      if (_hideButtonController.positions
          .any((pos) => pos.userScrollDirection == ScrollDirection.reverse)) {
      } else {
        if (_hideButtonController.positions
            .any((pos) => pos.userScrollDirection == ScrollDirection.forward)) {
        }
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            appBar: AppBar(
              title: Text('DataTable'),
            ),
            body: FutureBuilder(
              future: generateList(),
              builder: (context, AsyncSnapshot snapShot) {
                if (snapShot.hasData) {
                  return ListView.builder(
                      shrinkWrap: true,
                      scrollDirection: Axis.vertical,
                      itemCount: snapShot.data.length,
                      itemBuilder: (BuildContext context, int index) {
                        final isVisible = Random().nextBool(); //Random visibility, update this condition to your own. 
                        return SizedBox(
                          height: 250,
                          child: Stack(
                            children: [
                              CustomScrollView(
                                controller: _hideButtonController,
                                scrollDirection: Axis.horizontal,
                                shrinkWrap: true,
                                slivers: <Widget>[
                                  SliverPadding(
                                    padding: const EdgeInsets.all(20.0),
                                    sliver: SliverList(
                                      delegate: SliverChildListDelegate(
                                        <Widget>[
                                          Center(
                                            child: Text(snapShot.data[index]
                                            ["long-text"]
                                                .toString()),
                                          )
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                              Visibility(
                                visible: isVisible,
                                child: Align(
                                  alignment: Alignment.centerRight,
                                  child: FloatingActionButton(
                                      child:
                                      const Icon(Icons.arrow_forward_ios),
                                      onPressed: () {}),
                                ),
                              )
                            ],
                          ),
                        );
                      });
                } else {
                  return CircularProgressIndicator();
                }
              },
            )));
  }
}

And the _isVisible variable is not really useful and can be removed.

Solution 2:[2]

import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/rendering.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final ThemeData? themeData;
  const MyApp({Key? key, this.themeData}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: AppView2(),
    );
  }
}

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

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

class _AppViewState extends State<AppView2> {
  late ScrollController _hideButtonController;

  var isVisible = true;
  Future<List<LongText>> generateList() async {
    var data = await http.get(Uri.parse('https://jsonkeeper.com/b/S97W'));
    List<LongText> list = jsonDecode(data.body)
        .map<LongText>((e) => LongText.fromJson(e))
        .toList();
    return list;
  }

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            appBar: AppBar(
              title: Text('DataTable'),
            ),
            body: FutureBuilder(
              future: generateList(),
              builder: (context, AsyncSnapshot<List<LongText>> snapShot) {
                if (snapShot.hasData) {
                  return ListView.builder(
                      shrinkWrap: true,
                      scrollDirection: Axis.vertical,
                      itemCount: snapShot.data!.length,
                      itemBuilder: (BuildContext context, int index) {
                        return SizedBox(
                          height: 250,
                          child: Stack(
                            children: [
                              CustomScrollView(
                                controller: snapShot
                                    .data![index].scrollContoller
                                  ..addListener(() {
                                    if (snapShot.data![index].scrollContoller
                                            .position.userScrollDirection ==
                                        ScrollDirection.forward) {
                                      setState(() {
                                        snapShot.data![index].isVisibile = true;
                                      });
                                    } else {
                                      setState(() {
                                        snapShot.data![index].isVisibile =
                                            false;
                                      });
                                    }
                                  }),
                                scrollDirection: Axis.horizontal,
                                shrinkWrap: true,
                                slivers: <Widget>[
                                  SliverPadding(
                                    padding: const EdgeInsets.all(20.0),
                                    sliver: SliverList(
                                      delegate: SliverChildListDelegate(
                                        <Widget>[
                                          Center(
                                            child: Text(
                                              snapShot.data![index].longText,
                                            ),
                                          )
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                              Visibility(
                                visible: snapShot.data![index].isVisibile,
                                child: Align(
                                  alignment: Alignment.centerRight,
                                  child: FloatingActionButton(
                                    child: const Icon(Icons.arrow_forward_ios),
                                    onPressed: () {
                                      snapShot.data![index].scrollContoller
                                          .animateTo(
                                        snapShot.data![index].scrollContoller
                                            .position.maxScrollExtent,
                                        duration:
                                            const Duration(milliseconds: 500),
                                        curve: Curves.easeOut,
                                      );
                                    },
                                  ),
                                ),
                              )
                            ],
                          ),
                        );
                      });
                } else {
                  return CircularProgressIndicator();
                }
              },
            )));
  }
}

class LongText {
  final String longText;
  LongText({
    required this.longText,
    required this.isVisibile,
    required this.scrollContoller,
  });
  bool isVisibile;
  final ScrollController scrollContoller;
  LongText.fromJson(Map<String, dynamic> json)
      : longText = json['long-text'],
        isVisibile = true,
        scrollContoller = ScrollController();
}

Solution 3:[3]

enter image description here

  • User visibility per item Not on a global level
  • User _scrollcontroller per item (locally) as you are using it globally. So common controller will assign to all.

Here I provide an example of that.

  import 'dart:convert';
  import 'package:flutter/material.dart';
  import 'package:flutter/rendering.dart';
  import 'package:flutter/services.dart';
  
  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    runApp(MyApp());
  }
  
  class MyApp extends StatelessWidget {
    final ThemeData? themeData;
    const MyApp({Key? key, this.themeData}) : super(key: key);
  
    @override
    Widget build(BuildContext context) {
      return const MaterialApp(
        title: 'Flutter Demo',
        home: AppView2(),
      );
    }
  }
  
  class AppView2 extends StatefulWidget {
    const AppView2({
      Key? key,
    }) : super(key: key);
  
    @override
    _AppViewState createState() => _AppViewState();
  }
  
  class _AppViewState extends State<AppView2> {
    late ScrollController _hideButtonController;
    List<String> listData = [];
    var _isVisible;
    String dummyText =
        "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.";
  
    Future generateList() async {
      listData = [dummyText, dummyText, dummyText, dummyText];
      return listData;
    }
  
    @override
    void initState() {
      generateList();
      _isVisible = true;
  
      super.initState();
    }
  
    @override
    Widget build(BuildContext context) {
      return SafeArea(
          child: Scaffold(
              appBar: AppBar(
                title: Text('DataTable'),
              ),
              body: FutureBuilder(
                future: generateList(),
                builder: (context, AsyncSnapshot snapShot) {
                  if (snapShot.hasData) {
                    return ListView.builder(
                        shrinkWrap: true,
                        scrollDirection: Axis.vertical,
                        itemCount: snapShot.data.length,
                        itemBuilder: (BuildContext context, int index) {
                          ScrollController _hideButtonController =
                              ScrollController();
                          _hideButtonController.addListener(() {
                            if (_hideButtonController.positions.any((pos) =>
                                pos.userScrollDirection ==
                                ScrollDirection.reverse)) {
                              print(_hideButtonController.positions.first);
  
                              if (_isVisible == true) {
                                setState(() {
                                  _isVisible = false;
                                });
                              } else if (_isVisible == false) {
                                Future.delayed(const Duration(milliseconds: 1000),
                                    () {
                                  setState(() {
                                    _isVisible = true;
                                  });
                                });
                              }
                            } else {
                              if (_hideButtonController.positions.any((pos) =>
                                  pos.userScrollDirection ==
                                  ScrollDirection.forward)) {
                                if (_isVisible == true) {
                                  setState(() {
                                    _isVisible = false;
                                  });
                                } else if (_isVisible == false) {
                                  Future.delayed(
                                      const Duration(milliseconds: 1000), () {
                                    setState(() {
                                      _isVisible = true;
                                    });
                                  });
                                }
                              }
                            }
                          });
                          return SizedBox(
                            height: 250,
                            child: Stack(
                              children: [
                                CustomScrollView(
                                  controller: _hideButtonController,
                                  scrollDirection: Axis.horizontal,
                                  shrinkWrap: true,
                                  slivers: <Widget>[
                                    SliverPadding(
                                      padding: const EdgeInsets.all(20.0),
                                      sliver: SliverList(
                                        delegate: SliverChildListDelegate(
                                          <Widget>[
                                            Center(
                                              child: Text(snapShot.data[index]
                                                  .toString()),
                                            )
                                          ],
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                                Visibility(
                                  visible: _isVisible,
                                  child: Align(
                                    alignment: Alignment.centerRight,
                                    child: FloatingActionButton(
                                        child:
                                            const Icon(Icons.arrow_forward_ios),
                                        onPressed: () {}),
                                  ),
                                )
                              ],
                            ),
                          );
                        });
                  } else {
                    return CircularProgressIndicator();
                  }
                },
              )));
    }
  }
  
  class ListModel {
    String title;
    bool isVisible;
    ListModel(this.title, this.isVisible);
  }

Solution 4:[4]

If I understand correctly what you're looking for, you first need to split the source string.

You could apply the following to your specs column. What this does is:

  1. splits the string by ,
  2. sorts the elements of the string
  3. collapses the string, uniting its elements with ,
library("dplyr", warn.conflicts = FALSE)
specs <- c("arlo, bird",
           "tato, bird",
           "arlo, unk",
           "unk, glor",
           "glor, unk",
           "glor, unk",
           "tado, arlo, glor",
           "glor, unk",
           "unk, glor",
           "glor, arlo, bird")

purrr::map_chr(stringr::str_split(specs, ", "),
               .f = function(x) {
                 x %>%
                 stringr::str_sort() %>%
                   stringr::str_c(collapse = ", ")
                 })
#>  [1] "arlo, bird"       "bird, tato"       "arlo, unk"        "glor, unk"       
#>  [5] "glor, unk"        "glor, unk"        "arlo, glor, tado" "glor, unk"       
#>  [9] "glor, unk"        "arlo, bird, glor"

Created on 2022-01-10 by the reprex package (v2.0.1)

Starting from your data frame, the following should achieve what you're looking for:


nest_use %>%
  mutate(sorted = purrr::map_chr(
    stringr::str_split(specs, ", "),
    .f = function(x) {
      x %>%
        stringr::str_sort() %>%
        stringr::str_c(collapse = ", ")
    }))

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 DiyorbekDev
Solution 3
Solution 4 giocomai