1

this is a typical question that might be considered as low quality but I have been on this for about two hours, and I am just trying to understand this piece of code better, so instead of just telling me how to fix, could you please also explain a bit what is happening. I am sure that for someone more experienced that me, should be very easy to spot.

I am trying to make a scrollable list, and draw each row of the list, and be able to click in each row item. But my app draws all the items but I am only able to see some of the items, as much as the screen allows, which means it is not scrollable.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Some App Page'),
    ),
    body: ListView(
      children: <Widget>[
        Stack(
          alignment: const Alignment(1.0, 1.0),
          children: <Widget>[
            TextField(
              controller: cityController,
              keyboardType: TextInputType.number,
              decoration: const InputDecoration(hintText: 'Enter city...'),
            ),
            TextButton(
              onPressed: () {
                cityController.clear();
              },
              child: const Icon(Icons.clear),
            ),
          ],
        ),
        ElevatedButton(
          onPressed: () {
            _futureTime = fetchTimes(int.parse(cityController.text));
            if (cityController.text.isNotEmpty) {
              setState(() {
                cityController.clear(); // Clear value
              }); // clear the textField
              FocusScope.of(context)
                  .requestFocus(FocusNode()); // hide the keyboard
            }
          },
          child: const Text('Get City', style: TextStyle(fontSize: 20)),
        ),
        Column(
          children: <Widget>[
            Center(
              child: FutureBuilder<Times>(
                future: _futureTime,
                builder: (context, snapshot) {
                  if (!snapshot.hasData) {
                    return const CircularProgressIndicator();
                  }
                  return ListView.builder(
                    scrollDirection: Axis.vertical,
                    shrinkWrap: true,
                    itemBuilder: (BuildContext context, int index) {
                      return myTimeCard( date, index);
                    },
                    itemCount: data == null ? 0 : data.length,
                  );
                },
              ),
            ),
          ],
        ),
      ],
    ),
  );
}

Widget myTimeCard(String date, int index) {
  return InkWell(
    onTap: () {
      // Navigate to the next page & pass data.
      print("tapped, -> " + index.toString()); // for debugging purpose!
    },
    child: Stack(
      children: <Widget>[
        Opacity(
          opacity: 1,
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(8.0),
            ),
          ),
        ),
        Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(right: 16.0),
                  child: Text(
                    index.toString(),
                    style: const TextStyle(
                        color: Colors.black,
                        fontSize: 22.0,
                        fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            )
          ],
        )
      ],
    ),
  );
}

4 Answers 4

1

You are using two ListView s nested inside each other. In such cases you may need to let the Flutter know which ListView is the primary one. So, there is a property called primary. Try to set primary to false for the inner Listview.

return ListView.builder(
                primary: false,
                scrollDirection: Axis.vertical,
                shrinkWrap: true,
                itemBuilder: (BuildContext context, int index) {
                  return myTimeCard( date, index);
                },
                itemCount: data == null ? 0 : data.length,
              );
Sign up to request clarification or add additional context in comments.

Comments

1

The code you shared does not compile because I do not have additional context, so I had to spend some time to be able to make it compile, please make sure to provide a compilable code in the future.

the problem you're facing is because the main ListView is taking control of the scroll, to see the effect try scrolling by holding the screen from the button Get City.

There are many ways to solve this problem, depending on your goal, do you want to make the whole screen scrollable, or just the data list

Way 1. Make the whole screen scrollable: by keeping the control of the scroll in the main ListView, and making all the descending widgets non-scrollable, which in your case, by making the widget that wraps the data a Column instead of ListView:

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final TextEditingController cityController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Some App Page'),
      ),
      body: ListView(
        children: <Widget>[
          Stack(
            alignment: const Alignment(1.0, 1.0),
            children: <Widget>[
              TextField(
                controller: cityController,
                keyboardType: TextInputType.number,
                decoration: const InputDecoration(hintText: 'Enter city...'),
              ),
              TextButton(
                onPressed: () {
                  cityController.clear();
                },
                child: const Icon(Icons.clear),
              ),
            ],
          ),
          ElevatedButton(
            onPressed: () {
              _futureTime = fetchTimes(int.parse(cityController.text));
              if (cityController.text.isNotEmpty) {
                setState(() {
                  cityController.clear(); // Clear value
                }); // clear the textField
                FocusScope.of(context)
                    .requestFocus(FocusNode()); // hide the keyboard
              }
            },
            child: const Text('Get City', style: TextStyle(fontSize: 20)),
          ),
          Column(
            children: <Widget>[
              Center(
                child: FutureBuilder<Times>(
                  future: _futureTime,
                  builder: (context, snapshot) {
                    // if (!snapshot.hasData) {
                    //   return const CircularProgressIndicator();
                    // }

                    final data =

                        // snapshot.data;
                        List.generate(50, (index) => index.toString());

                    return Column(
                      children: [
                        for (int i = 0; i < data.length; i++)
                          myTimeCard(data[i], i)
                      ],
                    );
                  },
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  Widget myTimeCard(String date, int index) {
    return InkWell(
      onTap: () {
        // Navigate to the next page & pass data.
        print("tapped, -> " + index.toString()); // for debugging purpose!
      },
      child: Stack(
        children: <Widget>[
          Opacity(
            opacity: 1,
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8.0),
              ),
            ),
          ),
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.only(right: 16.0),
                    child: Text(
                      index.toString(),
                      style: const TextStyle(
                          color: Colors.black,
                          fontSize: 22.0,
                          fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              )
            ],
          )
        ],
      ),
    );
  }
}

Way 2. make the non-data widgets non-scrollable, and keep the scroll control in the data widget: can be done by converting the main ListView to a non-scrollable Widget (in your case Column), and wrapping the data list in Expanded widget, so it takes all the space it can have (for more info about Expanded):

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final TextEditingController cityController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Some App Page'),
      ),
      body: Column(
        children: <Widget>[
          Stack(
            alignment: const Alignment(1.0, 1.0),
            children: <Widget>[
              TextField(
                controller: cityController,
                keyboardType: TextInputType.number,
                decoration: const InputDecoration(hintText: 'Enter city...'),
              ),
              TextButton(
                onPressed: () {
                  cityController.clear();
                },
                child: const Icon(Icons.clear),
              ),
            ],
          ),
          ElevatedButton(
            onPressed: () {
              _futureTime = fetchTimes(int.parse(cityController.text));
              if (cityController.text.isNotEmpty) {
                setState(() {
                  cityController.clear(); // Clear value
                }); // clear the textField
                FocusScope.of(context)
                    .requestFocus(FocusNode()); // hide the keyboard
              }
            },
            child: const Text('Get City', style: TextStyle(fontSize: 20)),
          ),
          FutureBuilder<Times>(
            future: _futureTime,
            builder: (context, snapshot) {
              // if (!snapshot.hasData) {
              //   return const CircularProgressIndicator();
              // }

              final data =

                  // snapshot.data;
                  List.generate(50, (index) => index.toString());

              return Expanded(
                child: ListView.builder(
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  itemBuilder: (BuildContext context, int index) {
                    return myTimeCard(date, index);
                  },
                  itemCount: data == null ? 0 : data.length,
                ),
              );
            },
          ),
        ],
      ),
    );
  }

  Widget myTimeCard(String date, int index) {
    return InkWell(
      onTap: () {
        // Navigate to the next page & pass data.
        print("tapped, -> " + index.toString()); // for debugging purpose!
      },
      child: Stack(
        children: <Widget>[
          Opacity(
            opacity: 1,
            child: Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(8.0),
              ),
            ),
          ),
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.only(right: 16.0),
                    child: Text(
                      index.toString(),
                      style: const TextStyle(
                          color: Colors.black,
                          fontSize: 22.0,
                          fontWeight: FontWeight.bold),
                    ),
                  ),
                ],
              )
            ],
          )
        ],
      ),
    );
  }
}

1 Comment

Much appreciated.
0

The issue is coming because we have two scrollable ListView. While both of them are scrollable, while scrolling when the inner ListView it gets focused and parent become unfocus and scroll event only effect on inner ListView and you can't rollback to parent ListView, A simple solution will be using NeverScrollableScrollPhysics on inner ListView.builder.

child: ListView.builder(
  physics: NeverScrollableScrollPhysics(),
  scrollDirection: Axis.vertical,

2 Comments

It fixes the problem that I asked, I appreciate all the answers. Since we are here, suppose that I want only the List with the items to scroll and the button + input field be fastened on top of the scree, how can I do that in theory?
You can just replace top ListView with Column and wrap inner ListView with Expanded and it's Column with Flexible widget. Column<Stack, ..,Flexible<Column<Expanded< ListView.builder>>>>
0
singleChildScrollView(
child: ListView.builder(
   sinkwrap:true,
  physics: NeverScrollableScrollPhysics(),
  scrollDirection: Axis.vertical,)
)

Simple and Easy

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.