Как заставить виджет работать быстрее — пользовательский виджет преобразования json

#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 не несет ответственности и не загружает лениво. Для большого объема данных рекомендуется разбиение списка на страницы.