4

I needed to create the DialogItem widgets using the data that comes from the database. I tried to use for(){} but it did not work.

Could you help me solve this problem?

I put the used code as well as the evidence that works, just does not work the dynamic list of DialogItem with the data of the database.

To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:

dependencies:
  sqflite: any
  path_provider: any
  flutter:
    sdk: flutter

The DatabaseClient class will create the database with 3 records.

In the gif only foo1 appears the correct one would be to appear all the values of the list that came from the database:

[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]

enter image description here

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  int number;
  List listCategory;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.countCategory().then((list){
          this.number = list[0][0]['COUNT(*)']; //3
          this.listCategory = list[1];
          //[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
        });
      }
    );
  }

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

  void showCategoryDialog<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(),
      body: new Center(
        child: new RaisedButton(
          onPressed: (){           
            showCategoryDialog<String>(
              context: context,
              child: new SimpleDialog(
                title: const Text('Categories'),
                children: <Widget>[
                  //for(var i = 0; i < this.number; i++) {
                    new DialogItem(
                      icon: Icons.brightness_1,
                      color: this.colors[
                        this.listCategory[0]['color']
                        //the zero should be dynamic going from 0 to 2 with the for(){}
                        //but o for(){} dont work
                      ],
                      text: this.listCategory[0]['name'],
                      onPressed: () {
                        Navigator.pop(context, this.listCategory[0]['name']);
                      }
                    ),
                  //}                  
                ]
              )
            );
          },
          child: new Text("ListButton"),
        )
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
            CREATE TABLE category (
              id INTEGER PRIMARY KEY,
              name TEXT NOT NULL,
              color INTEGER NOT NULL
            )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future countCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    var count = await db.rawQuery("SELECT COUNT(*) FROM category");
    List list = await db.rawQuery('SELECT name, color FROM category');
    await db.close();

    return [count, list];
  }
}

//Class of Dialog Item
class DialogItem extends StatelessWidget {
  DialogItem({ 
    Key key,
    this.icon,
    this.size,
    this.color,
    this.text,
    this.onPressed }) : super(key: key);

  final IconData icon;
  double size = 36.0;
  final Color color;
  final String text;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return new SimpleDialogOption(
      onPressed: onPressed,
      child: new Container(
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            new Container(              
              child: new Container(
                margin: size == 16.0 ? new EdgeInsets.only(left: 7.0) : null,
                child: new Icon(icon, size: size, color: color),
              )                
            ),        
            new Padding(
              padding: size == 16.0 ?
                const EdgeInsets.only(left: 17.0) :
                const EdgeInsets.only(left: 16.0),
              child: new Text(text),
            ),
          ],
        ),
      )
    );
  }
}
rafaelcb21
  • 12,422
  • 28
  • 62
  • 86

2 Answers2

4

There might be other issues, but as a start, I think this code

for(var i = 0; i < this.number; i++) {
  ...
}

should be changed to

children: this.number == null ? null :  
  new List(this.number).map((i) => 
    new DialogItem(
      icon: Icons.brightness_1,
      color: this.colors[
        this.listCategory[0]['color']
        //the zero should be dynamic going from 0 to 2 with the for(){}
        //but o for(){} dont work
      ],
      text: this.listCategory[0]['name'],
      onPressed: () {
        Navigator.pop(context, this.listCategory[0]['name']);
      }
    ).toList(),

to not throw an exception when this.number is null (response not yet received from the database).

and wrap the code that updates the state with setState(() {...})

  createdb() async {
    await _db.create().then(
      (data){
        _db.countCategory().then((list){
          setState(() {
            this.number = list[0][0]['COUNT(*)']; //3
            this.listCategory = list[1];
          //[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
          });
        });
      }
    );
  }
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • in `for()` appears the errors, `[dart] Expected to find ')'.` `[dart] Expected to find ']'.` and `[dart] Expected an identifier.` But it closes correctly, it seems that does not let put `if()` or `for()` inside a `children list: []` – rafaelcb21 Nov 06 '17 at 16:03
  • Hm, I don't think one can use a `for` in a `List` constructor, you probably could do something [like this](https://dartpad.dartlang.org/44168616e8bb31e94f8021ec261f3f5b). – Maximilian Riegler Nov 06 '17 at 16:05
  • Duh, I totally missed, that you don't need to build the list by hand lol - great solution :) – Maximilian Riegler Nov 06 '17 at 16:23
  • Thanks for your help, but the `new List(this.number).map((i) =>...` solution is giving the following error `Invalid argument(s)` `_List.[] (dart:core-patch/dart:core/array.dart:11)` the error occurs when I tap the button – rafaelcb21 Nov 07 '17 at 09:03
  • Is this at runtime or in the IDE? – Günter Zöchbauer Nov 07 '17 at 09:05
  • Occurs in the debug of the app in runtime, but the error appear in IDE, not displaying the `ShowDialog`. – rafaelcb21 Nov 07 '17 at 10:06
  • 1
    But thanks to your idea of generating the list instead of generating the elements of the list I found the solution based on other stackflows – rafaelcb21 Nov 07 '17 at 10:06
  • Sorry, I don't know. – Günter Zöchbauer Nov 07 '17 at 10:07
1

I found the solution, according to the Flutter - Build Widgets dynamically and Flutter - Combine dynamically generated elements with hard-coded ones and in this question

As SimpleDialog only accepts a List of type Widget - <Widget>[] I declare a variable tiles of type List<Widget> - List<Widget> tiles; and created a function of type List<Widget> - List<Widget> buildTile(int counter) {... - to be able to return a List<Widget>

Because of Navigator.pop (context, ... I needed to create the buildTile() function inside the Widget build(BuildContext context) {...

In the buildTile() function I added a for() to insert into the Widget type list as many DialogItem Widgets were needed, according to the result that comes from the database

and wrap the code that updates the state with setState(() {...}) as explained by Günter Zöchbauer

setState(() {
  this.number = list[0][0]['COUNT(*)']; //3
  this.listCategory = list[1];
  //[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
})

The complete code working as well as the demo are below:

To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:

dependencies:
  sqflite: any
  path_provider: any
  flutter:
    sdk: flutter

enter image description here

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DatabaseClient _db = new DatabaseClient();
  int number;
  List listCategory;
  List<Widget> tiles;

  List colors = [
    const Color(0xFFFFA500),
    const Color(0xFF279605),
    const Color(0xFF005959)
  ];

  createdb() async {
    await _db.create().then(
      (data){
        _db.countCategory().then((list){
          setState(() {
            this.number = list[0][0]['COUNT(*)']; //3
            this.listCategory = list[1];            
            //[{name: foo1, color: 0}, {name: foo2, color: 1}, {name: foo3, color: 2}]
          });          
        });
      }
    );
  }

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

  void showCategoryDialog<T>({ BuildContext context, Widget child }) {
    showDialog<T>(
      context: context,
      child: child,
    )
    .then<Null>((T value) {
      if (value != null) {
        setState(() { print(value); });
      }
    });
  }

  @override
  Widget build(BuildContext context) {

    List<Widget> buildTile(int counter) {
      this.tiles = [];
      for(var i = 0; i < counter; i++) {
        this.tiles.add(
          new DialogItem(
            icon: Icons.brightness_1,
            color: this.colors[
              this.listCategory[i]['color']
            ],
            text: this.listCategory[i]['name'],
            onPressed: () {
              Navigator.pop(context, this.listCategory[i]['name']);
            }
          )
        );
      }
      return this.tiles;
    }

    return new Scaffold(
      appBar: new AppBar(),
      body: new Center(
        child: new RaisedButton(
          onPressed: (){           
            showCategoryDialog<String>(
              context: context,
              child: new SimpleDialog(
                title: const Text('Categories'),
                children: buildTile(this.number)
              )
            );
          },
          child: new Text("ListButton"),
        )
      ),
    );
  }
}

//Creating Database with some data and two queries
class DatabaseClient {
  Database db;

  Future create() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    db = await openDatabase(dbPath, version: 1, onCreate: this._create);
  }

  Future _create(Database db, int version) async {
    await db.execute("""
            CREATE TABLE category (
              id INTEGER PRIMARY KEY,
              name TEXT NOT NULL,
              color INTEGER NOT NULL
            )""");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
  }

  Future countCategory() async {
    Directory path = await getApplicationDocumentsDirectory();
    String dbPath = join(path.path, "database.db");
    Database db = await openDatabase(dbPath);

    var count = await db.rawQuery("SELECT COUNT(*) FROM category");
    List list = await db.rawQuery('SELECT name, color FROM category');
    await db.close();

    return [count, list];
  }
}

//Class of Dialog Item
class DialogItem extends StatelessWidget {
  DialogItem({ 
    Key key,
    this.icon,
    this.size,
    this.color,
    this.text,
    this.onPressed }) : super(key: key);

  final IconData icon;
  double size = 36.0;
  final Color color;
  final String text;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return new SimpleDialogOption(
      onPressed: onPressed,
      child: new Container(
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            new Container(              
              child: new Container(
                margin: size == 16.0 ? new EdgeInsets.only(left: 7.0) : null,
                child: new Icon(icon, size: size, color: color),
              )                
            ),        
            new Padding(
              padding: size == 16.0 ?
                const EdgeInsets.only(left: 17.0) :
                const EdgeInsets.only(left: 16.0),
              child: new Text(text),
            ),
          ],
        ),
      )
    );
  }
}
rafaelcb21
  • 12,422
  • 28
  • 62
  • 86