Q. Eude
Q. Eude

Reputation: 881

Listview that contain fixed listview

I would like to basically make a Settings Screen. Actually my screen look like that with Settings section that are composed of one title and a non-scrollable list view. I would like to put these sections in a list view to make the screen scrollable and to make every settings section following the last one.

Here is my actual screen.

Actual screen

And here is my code.

Widget build(BuildContext context) {
return Container(
    child: Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  mainAxisAlignment: MainAxisAlignment.start,
  mainAxisSize: MainAxisSize.min,
  children: <Widget>[
        Expanded(
            child: SettingSection('Communication', [
          SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
          SettingsPayload('Notifications', 'Switch'),
        ])),
        Expanded(
            child: SettingSection('Hello', [
          SettingsPayload('Email', 'Normal', () => print('value'),
              Icons.arrow_forward_ios),
          SettingsPayload('Notifications', 'Normal', () => print('hello')),
        ]))
  ],
));

How I build each list

buildlistSettings() {
return ListView.builder(
    physics: ScrollPhysics(parent: NeverScrollableScrollPhysics()),
    padding: const EdgeInsets.all(15.0),
    itemBuilder: (context, i) {
      if (i < widget.listSettings.length) {
        return Column(
          children: <Widget>[
            buildTileSettings(widget.listSettings[i]),
            Divider(),
          ],
        );
      }
    });
  }

@override


Widget build(BuildContext context) {
    return Container(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Container(
            padding: EdgeInsets.only(left : 10.0),
              height: 25.0,
              decoration:
                  BoxDecoration(color: Color.fromRGBO(223, 230, 233, 0.5), border: Border(bottom: BorderSide(color: Color.fromRGBO(223, 230, 233, 0.5), width: 1.0))),
              child: Row(children: <Widget>[Text(
                widget.title,
                style: TextStyle(fontSize: 15.0, color: Colors.blueGrey), 
              )])),
          Expanded(child: buildlistSettings()),
        ],
      ),
    );
  }
}

Upvotes: 3

Views: 3383

Answers (1)

Derek Lakin
Derek Lakin

Reputation: 16319

I think there's a set of problems here not least of which is trying to put a ListView inside a ListView (and having to turn off scrolling), but mostly the use of Expanded which tries to fill up all the space in the relevant direction, but inside another container, which is itself unbounded.

I would use a ListView at the root, then build up the children using simple Column, ListTile and Divider widgets. Here's a working example (duplicated your data to ensure there's enough to scroll):

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: ListView(
        children: <Widget>[
          SettingSection('Communication', [
            SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
            SettingsPayload('Notifications', 'Switch'),
          ]),
          SettingSection('Hello', [
            SettingsPayload('Email', 'Normal', () => print('value'),
                Icons.arrow_forward_ios),
            SettingsPayload('Notifications', 'Normal', () => print('hello')),
          ]),
          SettingSection('Communication', [
            SettingsPayload('Email', 'Switch', () => (print('Function :)'))),
            SettingsPayload('Notifications', 'Switch'),
          ]),
          SettingSection('Hello', [
            SettingsPayload('Email', 'Normal', () => print('value'),
                Icons.arrow_forward_ios),
            SettingsPayload('Notifications', 'Normal', () => print('hello')),
          ]),
        ],
      ),
    );
  }
}

class SettingSection extends StatelessWidget {
  final String title;
  final List<SettingsPayload> payload;

  SettingSection(this.title, this.payload);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Container(
              padding: EdgeInsets.only(left: 10.0),
              height: 25.0,
              decoration: BoxDecoration(
                  color: Color.fromRGBO(223, 230, 233, 0.5),
                  border: Border(
                      bottom: BorderSide(
                          color: Color.fromRGBO(223, 230, 233, 0.5),
                          width: 1.0))),
              child: Row(children: <Widget>[
                Text(
                  this.title,
                  style: TextStyle(fontSize: 15.0, color: Colors.blueGrey),
                )
              ])),
          _buildListSettings(context),
        ],
      ),
    );
  }

  Widget _buildListSettings(BuildContext context) {
    List<Widget> items = List<Widget>();
    for (var setting in this.payload) {
      // TODO: Add logic here to determine what kind of setting widget to
      // create based on the payload.
      items.add(ListTile(
        title: Text(setting.title),
        trailing: Icon(setting.icon),
      ));
      items.add(Divider());
    }
    return Column(children: items);
  }
}

class SettingsPayload {
  final String title;
  final String type;
  final Function handler;
  final IconData icon;

  SettingsPayload(this.title, this.type, [this.handler, this.icon]);
}

You could use ListView.builder at the root level if your incoming settings data is variable and create SettingSection widgets for each item. Depends how you're handling your data.

Upvotes: 4

Related Questions