Вложенный ListView.Builder не работает при рендеринге - Flutter

Я пытаюсь использовать приложение флаттера для создания небольшой игры. У меня есть List<List<ColumnData>> gameBoard, где ColumnData — локально определенный класс. Предполагается, что список представляет собой двумерный список в формате треугольника, например. для уровня 3 список будет выглядеть так:

[00]
[10][11]
[20][21][22]

Мой код моделирования:

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',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}
class ColumnData {
  int _rowIdx;
  int _colIdx;
  String _data;
  Widget _column;

  ColumnData({String data, int row, int col}) {
    _data = data;
    _rowIdx = row;
    _colIdx = col;
    _column = new InkWell(
      child: new Container(
        margin: new EdgeInsets.all(10.0),
        child: new SizedBox(width: 30.0, height: 30.0,
          child: new Text(_data, textAlign: TextAlign.center),
        ),
        decoration: new BoxDecoration(
          border: new Border.all(color: Colors.indigoAccent, width: 2.0),
          shape: BoxShape.rectangle,
        ),
      ),
    );
  }
  set data(String value) => _data=value;
  set colIdx(int value) => _colIdx=value;
  set rowIdx(int value) => _rowIdx=value;
  Widget get column => _column;
  String get data => _data;
  int get colIdx => _colIdx;
  int get rowIdx => _rowIdx;
  String toString(){
    return ('Row: ${_rowIdx} Col: ${_colIdx} Data: $_data');
  }
}

class _MyHomePageState extends State<MyHomePage> {
  List<List<ColumnData>> gameBoard = new List(3);

  @override
  void initState() {
    super.initState();
    print('Lets build matrix');
    initGame();
  }
  @override
  Widget build(BuildContext context) {
    return new Scaffold(body: _buildBody());
  }

  Widget _buildBody() {
    return  new ListView.builder(
      scrollDirection: Axis.horizontal,
      itemCount: gameBoard.length,
      shrinkWrap: true,
      itemBuilder: (BuildContext ctx, int rowIdx){
        return new ListView.builder(
          scrollDirection: Axis.vertical,
          shrinkWrap: true,
          itemCount: gameBoard[rowIdx].length,
          itemBuilder: (BuildContext ctx, int colIdx){
            return gameBoard[rowIdx][colIdx].column;
          },
        );
      },
    );
  }

  initGame(){
    List<ColumnData> row1Data = [];
    List<ColumnData> row2Data = [];
    List<ColumnData> row3Data = [];
    ColumnData colData00 = new ColumnData(row: 0, col: 0, data: '7');
    row1Data.add(colData00);
    gameBoard[0] =row1Data;
    ColumnData colData10 = new ColumnData(row: 1, col: 0, data: '5');
    ColumnData colData11 = new ColumnData(row: 1, col: 1, data: '2');
    row2Data.add(colData10);
    row2Data.add(colData11);
    gameBoard[1]= row2Data;
    ColumnData colData20 = new ColumnData(row: 2, col: 0, data: '3');
    ColumnData colData21 = new ColumnData(row: 2, col: 1, data: '2');
    ColumnData colData22 = new ColumnData(row: 2, col: 2, data: '0');
    row3Data.add(colData20);
    row3Data.add(colData21);
    row3Data.add(colData22);
    gameBoard[2] = row3Data;
    print(gameBoard);
  }

}

Фрагмент ошибки, которую я получаю:

I/flutter (21273): [[Row: 0 Col: 0 Data: 7], [Row: 1 Col: 0 Data: 5, Row: 1 Col: 1 Data: 2], [Row: 2 Col: 0 Data: 3, Row: 2 Col: 1 Data: 2, Row: 2 Col: 2 Data: 0]]
Restarted app in 9,341ms.
I/flutter (21273): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (21273): The following assertion was thrown during performLayout():
I/flutter (21273): 'package:flutter/src/rendering/viewport.dart': Failed assertion: line 1283 pos 16:
I/flutter (21273): 'constraints.hasBoundedWidth': is not true. 
I/flutter (21273): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (21273): more information in this error message to help you determine and fix the underlying cause.
I/flutter (21273): In either case, please report this assertion by filing a bug on GitHub: 
I/flutter (21273):   https://github.com/flutter/flutter/issues/new
I/flutter (21273): When the exception was thrown, this was the stack:
I/flutter (21273): #2      RenderShrinkWrappingViewport.performLayout (package:flutter/src/rendering/viewport.dart)
I/flutter (21273): #3      RenderObject.layout (package:flutter/src/rendering/object.dart:1570:7)
......

Я пробовал без термоусадочной пленки, а также с использованием строк и столбцов, но безуспешно.

Что именно я делаю неправильно? Любые другие варианты, которые я могу изучить? Любой другой обходной путь/идея для построения 2-D матрицы

Любые указатели приветствуются

С уважением


person Dhaval Shah    schedule 05.05.2018    source источник
comment
ListView нужны границы. Содержимое ListView не ограничено, потому что это область прокрутки с неограниченной высотой. Если вы поместите в него список, вам нужно как-то ограничить высоту внутреннего списка. Ошибка утверждения говорит вам, что вы этого не сделали.   -  person Günter Zöchbauer    schedule 05.05.2018


Ответы (2)


ListView внутри ListView — плохая идея для начала.

Это типичный вариант использования CustomScrollView. Где вы можете объединить несколько прокруток в один блок. И по-прежнему иметь плавный виртуальный рендеринг, когда на экране отображается только то, что видно.

Это преобразует ваш метод build в следующее:

@override
Widget build(BuildContext context) {
  final slivers = new List<Widget>(gameBoard.length);

  for (var i = 0; i < gameBoard.length; i++) {
    slivers[i] = new SliverList(
      delegate: new SliverChildBuilderDelegate((context, j) {
        return gameBoard[i][j].column;
      }, childCount: gameBoard[i].length),
    );
  }
  return new Scaffold(
    body: new CustomScrollView(
      shrinkWrap: true,
      slivers: slivers,
    ),
  );
}
person Rémi Rousselet    schedule 05.05.2018
comment
Это создает макет линейным образом, то есть сверху вниз, тогда как мне нужен был двумерный макет (как сетка), где сетка имеет различное количество столбцов для каждой строки. Мне удалось сделать, как показано в моем ответе, потому что длина каждой строки конечна. Что, если в макете должно быть несколько строк (вертикальная прокрутка), а в каждой строке несколько столбцов (горизонтальная прокрутка), которые не являются окончательными? - person Dhaval Shah; 17.05.2018

Поскольку в моей игровой области было конечное количество строк и столбцов (разработанных на основе выбранного уровня и некоторых других параметров), я смог реализовать свой макет следующим образом:

Widget _buildBody() {
    List<Widget> rows = new List<Widget>(gameBoard.length);
    for (int i=0;i<gameBoard.length; i++){
      List<Widget> cols = new List<Widget>(gameBoard[i].length);
      for(int j=0; j<gameBoard[i].length; j++){
        cols[j] = gameBoard[i][j].column;
      }
      rows[i] = new Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: cols,
      );
    }

    return new Container(
      child: new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: rows,
      ),
    );
}

После этого вывод

введите здесь описание изображения

person Dhaval Shah    schedule 17.05.2018