#flutter #dart #web
Вопрос:
У меня есть список, в котором я хочу отобразить список объектов. Каждый объект имеет поле json, в котором определены «теги». Каждое из них состоит из имени поля и значения. Значения из полей, содержащихся в json, должны отображаться в приятной форме «плиток». Когда поле сфокусировано, должно появиться имя поля (всплывающая подсказка).
Я написал свой виджет, который преобразует json для этого. Работает нормально, но я обнаружил, что производительность списка сильно замедляется из-за количества элементов (прокрутка и наведение курсора на элемент). В 10 это все еще нормально, но в 50 это не так. Когда я комментирую «JsonWidget» и просто использую текстовый виджет, все работает быстро. Интересно, есть ли способ написать это более оптимальным способом.
У нее полный код:
import 'dart:convert'; import 'package:flutter/material.dart'; void main() { runApp(GenistryApp()); } //My example object with json field class MyObject { int id; String json = '{"year":1850,"1 name":"Zuzanna","names":"Stefania","surname":"Zabłocka","sex":"W","city":"Warsaw","date":"1850.01.02","father":"Piotr","mothers_anme":"Józefa","mothers_surname":"Wojnicz","info":"Szlachetni"}'; MyObject(this.id); } class GenistryApp extends StatelessWidget { const GenistryApp({Key key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: MyTableWidget(), theme: ThemeData( primarySwatch: Colors.lightGreen, visualDensity: VisualDensity.adaptivePlatformDensity, )); } } class MyTableWidget extends StatefulWidget { const MyTableWidget({Key key}) : super(key: key); @override _MyTableWidgetState createState() =gt; _MyTableWidgetState(); } class _MyTableWidgetState extends Statelt;MyTableWidgetgt; { MyDataTableSource _data = MyDataTableSource([]); Listlt;MyObjectgt; list = []; //Generating 50 same elements for testing getData() { for (int i = 0; i lt; 50; i ) { list.add(MyObject(i)); } _data = MyDataTableSource(list); } @override Widget build(BuildContext context) { getData(); return Scaffold( backgroundColor: Colors.white.withOpacity(0.8), body: SingleChildScrollView( scrollDirection: Axis.vertical, child: PaginatedDataTable( source: _data, columns: [ DataColumn(label: Container()), DataColumn( label: ConstrainedBox( constraints: BoxConstraints.expand( width: MediaQuery.of(context).size.width))), ], columnSpacing: 50, horizontalMargin: 10, rowsPerPage: 50, showCheckboxColumn: false, ), )); } } class MyDataTableSource extends DataTableSource { Listlt;MyObjectgt; _data; MyDataTableSource(this._data); bool get isRowCountApproximate =gt; false; int get rowCount =gt; _data.length; int get selectedRowCount =gt; 0; DataRow getRow(int index) { return DataRow(cells: [ DataCell(Text("ID")), DataCell( JsonWidget(_data[index] .json), // Here is my widget to convert json field - it slows all list // Text(_data[index].json), // Here is simple json field for testing - it works fast onTap: () =gt; {}, ), ]); } } //Widget to convert json to nice looking "tags" class JsonWidget extends StatelessWidget { String jdata; Maplt;String, dynamicgt; data; JsonWidget(String jdata) { this.jdata = jdata; } var containers = lt;Tooltipgt;[]; @override Widget build(BuildContext context) { this.data = json.decode(this.jdata); data.entries.forEach((element) { containers.add(new Tooltip( message: element.key, //tag's fieldname textStyle: TextStyle(color: Colors.white), child: Container( margin: const EdgeInsets.all(3), padding: const EdgeInsets.all(4), child: Text(element.value.toString()), //tag's fieldvalue decoration: BoxDecoration( border: Border.all(color: Colors.blueAccent, width: 0.7), borderRadius: BorderRadius.all(Radius.circular(20))), ))); }); return Row(children: this.containers); } }
Ответ №1:
Метод сборки разработан таким образом, чтобы он был чистым/без побочных эффектов. Он должен возвращать только виджет, а не выполнять циклы, как в случае с JsonWidget. Лучшим способом написать это будет использование listview builder.
class JsonWidget extends StatelessWidget { Maplt;String, dynamicgt; data; JsonWidget(Maplt;String, dynamicgt; data) { this.data = data; } var containers = lt;Tooltipgt;[]; @override Widget build(BuildContext context) { return ListView.builder( shrinkWrap: true, scrollDirection: Axis.horizontal, itemCount: data.entries.length, itemBuilder: (context, index) { return Tooltip( message: data.entries.toList()[index].key, //tag's fieldname textStyle: TextStyle(color: Colors.white), child: Container( margin: const EdgeInsets.all(3), padding: const EdgeInsets.all(4), child: Text(data.entries .toList()[index] .value .toString()), //tag's fieldvalue decoration: BoxDecoration( border: Border.all(color: Colors.blueAccent, width: 0.7), borderRadius: BorderRadius.all(Radius.circular(20))), )); }); } }
И MyDataTableSoure будет выглядеть так
class MyDataTableSource extends DataTableSource { Listlt;MyObjectgt; _data; MyDataTableSource(this._data); bool get isRowCountApproximate =gt; false; int get rowCount =gt; _data.length; int get selectedRowCount =gt; 0; DataRow getRow(int index) { return DataRow(cells: [ DataCell(Text("ID")), DataCell( JsonWidget(json.decode(_data[index] .json)), // Here is my widget to convert json field - it slows all list // Text(_data[index].json), // Here is simple json field for testing - it works fast onTap: () =gt; {}, ), ]); } }
Комментарии:
1. Спасибо за предложения. Это выглядит лучше, но не влияет на улучшение производительности. Он все еще медленный с 50 элементами.
2. Если вы попытаетесь использовать конструктор listview вместо цикла for. С помощью listview он лениво загружается
3. ДА. Сейчас определенно лучше. Может быть, вы знаете, правильно ли использовать Listview для большого объема данных с разбиением на страницы? Или Datatable лучше?
4. Конструктор ListView поддерживает отложенную загрузку, которая необходима для больших объемов данных, поскольку только данные, видимые на экране, загружаются с помощью ListView.builder и построения на основе индекса. В то время как Datatable не несет ответственности и не загружает лениво. Для большого объема данных рекомендуется разбиение списка на страницы.