flutter dynamic expansionTile

前端 未结 3 2017
南方客
南方客 2020-12-16 06:10

Looking for some guidance on building expansionTile list dynamically. I have a successful Listview built dynamically from json API, but can not find any examples on buildin

相关标签:
3条回答
  • 2020-12-16 06:33

    Following Rainer Wittmann approach, I modified it to fit my needs and implemented for Cloud Firestore, but instead of futures I used streams.

    My basic structure of Cloud Firestore is:

    Collection projects

    • name

    • Collection surveys:

      • surveyName

    Solution:

    class ProjectList extends StatelessWidget {
      ProjectList({this.firestore});
    
      final Firestore firestore;
    
      @override
      Widget build(BuildContext context) {
        return StreamBuilder<QuerySnapshot>(
          stream: firestore.collection('projects').snapshots(),
          builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
            if (!snapshot.hasData) return const Text('Loading...');
            //final int projectsCount = snapshot.data.documents.length;
            List<DocumentSnapshot> documents = snapshot.data.documents;
            return ExpansionTileList(
              firestore: firestore,
              documents: documents,
            );
          },
        );
      }
    }
    
    class ExpansionTileList extends StatelessWidget {
      final List<DocumentSnapshot> documents;
      final Firestore firestore;
    
      ExpansionTileList({this.documents, this.firestore});
    
      List<Widget> _getChildren() {
        List<Widget> children = [];
        documents.forEach((doc) {
          children.add(
            ProjectsExpansionTile(
              name: doc['name'],
              projectKey: doc.documentID,
              firestore: firestore,
            ),
          );
        });
        return children;
      }
    
      @override
      Widget build(BuildContext context) {
        return ListView(
          children: _getChildren(),
        );
      }
    }
    
    class ProjectsExpansionTile extends StatelessWidget {
      ProjectsExpansionTile({this.projectKey, this.name, this.firestore});
    
      final String projectKey;
      final String name;
      final Firestore firestore;
    
      @override
      Widget build(BuildContext context) {
        PageStorageKey _projectKey = PageStorageKey('$projectKey');
    
        return ExpansionTile(
          key: _projectKey,
          title: Text(
            name,
            style: TextStyle(fontSize: 28.0),
          ),
          children: <Widget>[
            StreamBuilder(
                stream: firestore
                    .collection('projects')
                    .document(projectKey)
                    .collection('surveys')
    
                    .snapshots(),
                builder:
                    (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
                  if (!snapshot.hasData) return const Text('Loading...');
                  //final int surveysCount = snapshot.data.documents.length;
                  List<DocumentSnapshot> documents = snapshot.data.documents;
    
                  List<Widget> surveysList = [];
                  documents.forEach((doc) {
                    PageStorageKey _surveyKey =
                        new PageStorageKey('${doc.documentID}');
    
                    surveysList.add(ListTile(
                      key: _surveyKey,
                      title: Text(doc['surveyName']),
                    ));
                  });
                  return Column(children: surveysList);
                })
          ],
        );
      }
    }
    

    Hope this helps to those lost in nested collections in cloud firestore.

    Happy coding!

    0 讨论(0)
  • 2020-12-16 06:44

    I think the best way to tackle this is to use a FutureBuilder (maybe have a look at this answer). I would implement one FutureBuilder for the ExpansionTile titles and a second one for each ExpansionTile body.
    The example in the above linked answer could be very well adapted to your use case.

    If you need more help, just shout and I will try to implement an example for you.

    0 讨论(0)
  • 2020-12-16 06:47

    Reacting to you comment and edit of the question I took the liberty to write a working example. Feel free to edit or comment. I hope, this is what you wanted to achieve.

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    import 'dart:convert';
    
    void main() {
      runApp(new MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'ExpansionTile Test',
          home: new MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      Future<http.Response> _responseFuture;
    
      @override
      void initState() {
        super.initState();
        _responseFuture = http.get('http://174.138.61.246:8080/support/dc/1');
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('ExpansionTile Test'),
          ),
          body: new FutureBuilder(
            future: _responseFuture,
            builder: (BuildContext context, AsyncSnapshot<http.Response> response) {
              if (!response.hasData) {
                return const Center(
                  child: const Text('Loading...'),
                );
              } else if (response.data.statusCode != 200) {
                return const Center(
                  child: const Text('Error loading data'),
                );
              } else {
                List<dynamic> json = JSON.decode(response.data.body);
                return new MyExpansionTileList(json);
              }
            },
          ),
        );
      }
    }
    
    class MyExpansionTileList extends StatelessWidget {
      final List<dynamic> elementList;
    
      MyExpansionTileList(this.elementList);
    
      List<Widget> _getChildren() {
        List<Widget> children = [];
        elementList.forEach((element) {
          children.add(
            new MyExpansionTile(element['did'], element['dname']),
          );
        });
        return children;
      }
    
      @override
      Widget build(BuildContext context) {
        return new ListView(
          children: _getChildren(),
        );
      }
    }
    
    class MyExpansionTile extends StatefulWidget {
      final int did;
      final String name;
      MyExpansionTile(this.did, this.name);
      @override
      State createState() => new MyExpansionTileState();
    }
    
    class MyExpansionTileState extends State<MyExpansionTile> {
      PageStorageKey _key;
      Future<http.Response> _responseFuture;
    
      @override
      void initState() {
        super.initState();
        _responseFuture =
            http.get('http://174.138.61.246:8080/support/dcreasons/${widget.did}');
      }
    
      @override
      Widget build(BuildContext context) {
        _key = new PageStorageKey('${widget.did}');
        return new ExpansionTile(
          key: _key,
          title: new Text(widget.name),
          children: <Widget>[
            new FutureBuilder(
              future: _responseFuture,
              builder:
                  (BuildContext context, AsyncSnapshot<http.Response> response) {
                if (!response.hasData) {
                  return const Center(
                    child: const Text('Loading...'),
                  );
                } else if (response.data.statusCode != 200) {
                  return const Center(
                    child: const Text('Error loading data'),
                  );
                } else {
                  List<dynamic> json = JSON.decode(response.data.body);
                  List<Widget> reasonList = [];
                  json.forEach((element) {
                    reasonList.add(new ListTile(
                      dense: true,
                      title: new Text(element['reason']),
                    ));
                  });
                  return new Column(children: reasonList);
                }
              },
            )
          ],
        );
      }
    }
    

    0 讨论(0)
提交回复
热议问题