本文章是总结渲染列表的具体步骤,代码参考于正在着手开发的项目
首先,想要从后端服务器接收数据并将这个数据渲染到页面上,需要先接收并定义类模型,,也就是从服务器接收的数据
这个类模型会自动生成一个 .g.dart 文件,当你修改接收到的数据时,也要在对应 .g.dart 文件修改
进行演示的代码片段为点击目标位置进行弹窗,然后在弹窗中渲染一个输入框和列表,所演示流程仅为接收并渲染数据,和其他功能无关。
1.接收并定义类模型
目录 security.dart
import 'package:collection/collection.dart';
import 'package:decimal/decimal.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:rational/rational.dart' show Rational;
import '../enums.dart';
part 'security.g.dart';//对应的.g.dart文件
@JsonSerializable()
class Security {
num? id;
String? code;
String? name;
String? acronym;
@JsonKey(name: 'exchange_type')
ExchangeType exchangeType;
@JsonKey(name: 'market_value')
String? marketValue;
String? pe;
@JsonKey(name: 'yesterday_price')
Decimal? yesterdayPrice;
@JsonKey(name: 'yesterday_range')
Decimal? yesterdayRange;
@JsonKey(name: 'lowest_price_by_dividend')
Decimal? lowestPriceByDividend;
@JsonKey(name: 'max_draw_down_by_dividend')
Decimal? maxDrawDownByDividend;
@JsonKey(name: 'lowest_price_by_profit')
Decimal? lowestPriceByProfit;
@JsonKey(name: 'max_draw_down_by_profit')
Decimal? maxDrawDownByProfit;
@JsonKey(name: 'revenue_year_growth_rate')
Decimal? revenueYearGrowthRate;
@JsonKey(name: 'profit_year_growth_rate')
Decimal? profitYearGrowthRate;
@JsonKey(name: 'dividend_rate')
String? dividendRate;
@JsonKey(name: 'dividend')
Decimal? dividend;
@JsonKey(name: 'by_people')
bool? byPeople;
@JsonKey(name: 'is_dividend')
bool? isDividend;
@JsonKey(name: 'is_holdings')
bool? isHoldings;
@JsonKey(name: 'security_type')
SecurityType? securityType;
@JsonKey(name: 'current_price')
Decimal? currentPrice;
@JsonKey(name: 'percent_change')
Decimal? percentChange;
@JsonKey(name: 'updated_at')
String? updatedAt;
@JsonKey(name: 'industry')
List<Industry>? industry;
Security(
this.id,
this.code,
this.name,
this.acronym,
this.exchangeType,
this.marketValue,
this.pe,
this.yesterdayPrice,
this.yesterdayRange,
this.lowestPriceByDividend,
this.maxDrawDownByDividend,
this.lowestPriceByProfit,
this.maxDrawDownByProfit,
this.revenueYearGrowthRate,
this.profitYearGrowthRate,
this.dividendRate,
this.dividend,
this.byPeople,
this.isDividend,
this.isHoldings,
this.securityType,
this.currentPrice,
this.percentChange,
this.updatedAt,
this.industry
);
String get yesterdayDisplay {
if (yesterdayRange == null || yesterdayPrice == null) {
return '--';
}
if (yesterdayRange! < Decimal.zero) {
return "$yesterdayPrice ($yesterdayRange%)";
}
return "$yesterdayPrice (+$yesterdayRange%)";
}
Decimal? dividendRateTodayDisplay(Decimal? currentPrice, Decimal? dividendRatio) {
if (dividend == null ||
dividendRatio == null ||
currentPrice == null ||
currentPrice <= Decimal.zero) {
return null;
}
Rational r = dividend! * dividendRatio! * Decimal.fromInt(100) / currentPrice;
return r.toDecimal(scaleOnInfinitePrecision: 3);
}
bool isFundType() {
if (securityType == null) {
return false;
}
return securityType! == SecurityType.securityETF ||
securityType! == SecurityType.securityLOF;
}
String get dividendRateDisplay {
if (dividendRate == null) {
return '--';
}
return "$dividendRate%";
}
@override
String toString() {
return 'Security(id: $id, code: $code, name: $name, acronym: $acronym, exchangeType: $exchangeType, marketValue: $marketValue)';
}
factory Security.fromJson(Map<String, dynamic> json) {
return _$SecurityFromJson(json);
}
Map<String, dynamic> toJson() => _$SecurityToJson(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
if (other is! Security) return false;
final mapEquals = const DeepCollectionEquality().equals;
return mapEquals(other.toJson(), toJson());
}
@override
int get hashCode =>
id.hashCode ^
code.hashCode ^
currentPrice.hashCode ^
percentChange.hashCode ^
securityType.hashCode;
}
@JsonSerializable()
class Industry {
@JsonKey(name: 'id')
int? id;
@JsonKey(name: 'ind_name')
String? indName;
Industry(this.id,this.indName,);
factory Industry.fromJson(Map<String, dynamic> srcJson) => _$IndustryFromJson(srcJson);
}
目录 security.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'security.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Security _$SecurityFromJson(Map<String, dynamic> json) => Security(
json['id'] as num?,
json['code'] as String?,
json['name'] as String?,
json['acronym'] as String?,
$enumDecode(_$ExchangeTypeEnumMap, json['exchange_type']),
json['market_value'] as String?,
json['pe'] as String?,
json['yesterday_price'] == null
? null
: Decimal.fromJson(json['yesterday_price'] as String),
json['yesterday_range'] == null
? null
: Decimal.fromJson(json['yesterday_range'] as String),
json['lowest_price_by_dividend'] == null
? null
: Decimal.fromJson(json['lowest_price_by_dividend'] as String),
json['max_draw_down_by_dividend'] == null
? null
: Decimal.fromJson(json['max_draw_down_by_dividend'] as String),
json['lowest_price_by_profit'] == null
? null
: Decimal.fromJson(json['lowest_price_by_profit'] as String),
json['max_draw_down_by_profit'] == null
? null
: Decimal.fromJson(json['max_draw_down_by_profit'] as String),
json['revenue_year_growth_rate'] == null
? null
: Decimal.fromJson(json['revenue_year_growth_rate'] as String),
json['profit_year_growth_rate'] == null
? null
: Decimal.fromJson(json['profit_year_growth_rate'] as String),
json['dividend_rate'] as String?,
json['dividend'] == null
? null
: Decimal.fromJson(json['dividend'] as String),
json['by_people'] as bool?,
json['is_dividend'] as bool?,
json['is_holdings'] as bool?,
$enumDecodeNullable(_$SecurityTypeEnumMap, json['security_type']),
json['current_price'] == null
? null
: Decimal.fromJson(json['current_price'] as String),
json['percent_change'] == null
? null
: Decimal.fromJson(json['percent_change'] as String),
json['updated_at'] as String?,
(json['industry'] as List<dynamic>?)
?.map((e) => Industry.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$SecurityToJson(Security instance) => <String, dynamic>{
'id': instance.id,
'code': instance.code,
'name': instance.name,
'acronym': instance.acronym,
'exchange_type': _$ExchangeTypeEnumMap[instance.exchangeType]!,
'market_value': instance.marketValue,
'pe': instance.pe,
'yesterday_price': instance.yesterdayPrice,
'yesterday_range': instance.yesterdayRange,
'lowest_price_by_dividend': instance.lowestPriceByDividend,
'max_draw_down_by_dividend': instance.maxDrawDownByDividend,
'lowest_price_by_profit': instance.lowestPriceByProfit,
'max_draw_down_by_profit': instance.maxDrawDownByProfit,
'revenue_year_growth_rate': instance.revenueYearGrowthRate,
'profit_year_growth_rate': instance.profitYearGrowthRate,
'dividend_rate': instance.dividendRate,
'dividend': instance.dividend,
'by_people': instance.byPeople,
'is_dividend': instance.isDividend,
'is_holdings': instance.isHoldings,
'security_type': _$SecurityTypeEnumMap[instance.securityType],
'current_price': instance.currentPrice,
'percent_change': instance.percentChange,
'updated_at': instance.updatedAt,
'industry': instance.industry,
};
const _$ExchangeTypeEnumMap = {
ExchangeType.sz: 'SZ',
ExchangeType.sh: 'SH',
ExchangeType.nq: 'NQ',
ExchangeType.bj: 'BJ',
};
const _$SecurityTypeEnumMap = {
SecurityType.securityStockA: 0,
SecurityType.securityStockB: 1,
SecurityType.securityETF: 2,
SecurityType.securityLOF: 3,
};
Industry _$IndustryFromJson(Map<String, dynamic> json) => Industry(
json['id'] as int?,
json['ind_name'] as String?,
);
Map<String, dynamic> _$IndustryToJson(Industry instance) => <String, dynamic>{
'id': instance.id,
'ind_name': instance.indName,
};
2. 定义一个接收APIRequester的类来获取列表数据
1.定义了一个名为 AssetsBuyRequest 的类,它继承自 APIRequester。
-
这个类中包含了两个方法:
searchconvertible方法用于模糊查询可转换证券,并返回PaginationResponse<Security>类型的数据。fetchAssetsBuy方法用于根据查询字符串获取证券列表,并返回ListResponse<Security>类型的数据。
3.这两个方法中,您都使用了 sendGetRequest 方法来发送 GET 请求到后端 API 接口,并根据响应结果构建相应的数据模型。
security_request.dart
这一段也属于第二步,
具体来说,这段代码是一个名为 AssetsBuyController 的类,它继承自 ListController<Security>。在这个类中,您定义了以下内容:
AssetsBuyRequest的实例_request,用于从服务器获取数据。getSecurityRewards方法,用于从服务器获取证券列表数据,并将数据存储在_securityList中,并输出一些证券信息。_securityList,这是一个RxList<Security>类型的响应式列表,用于存储证券数据。fetchDataFromNetwork方法,这是一个重写的方法,用于从网络获取数据,这也是在渲染列表时常见的操作。
因此,这段代码属于第二步,即在 Flutter 中渲染列表的步骤,用于控制数据的获取和管理。
AssetsBuyController.dart
不过
AssetsBuyController需要在binding中抛出,也就是注入依赖,让这个类可以在全局访问
binding.dart
3.将接收到的数据渲染到页面上(列表)
经过前面两个步骤,已经接收到服务器数据,接下来需要在需要的页面上进行渲染了
父级页面----这个页面是我需要渲染列表的父级页面,在这个页面中定义了我需要的弹窗。
assets_buy_input_widget.dart
子页面--在子页面中渲染数据