@@ -41,7 +41,7 @@ public class MainActivity extends FlutterActivity implements ZhiyingFlutterCommN | |||||
map.put("domain", "http://192.168.0.113:5000"); //"http://120.76.175.204:8989"); | map.put("domain", "http://192.168.0.113:5000"); //"http://120.76.175.204:8989"); | ||||
map.put("master_id", "123456"); | map.put("master_id", "123456"); | ||||
map.put("secret_key", "123456"); | map.put("secret_key", "123456"); | ||||
map.put("token", "123465"); | |||||
// map.put("token", "123465"); | |||||
return map; | return map; | ||||
} | } | ||||
@@ -0,0 +1,30 @@ | |||||
import 'package:shimmer/shimmer.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
/// | |||||
/// | |||||
/// | |||||
class LoginAccountSkeleton extends StatelessWidget { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Scaffold( | |||||
appBar: AppBar(), | |||||
body: Container( | |||||
width: double.infinity, | |||||
height: double.infinity, | |||||
), | |||||
); | |||||
} | |||||
Widget _shimmerWidget({double width, double height, double radius = 0}) { | |||||
return Shimmer.fromColors( | |||||
baseColor: Colors.grey[300], | |||||
highlightColor: Colors.grey[100], | |||||
child: Container( | |||||
width: width, | |||||
height: height, | |||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -82,7 +82,8 @@ class _LoginInvitePageContainerState extends State<LoginInvitePageContainer> { | |||||
String inviteNum = inviteUser?.userId ?? ''; | String inviteNum = inviteUser?.userId ?? ''; | ||||
/// 手机号 | /// 手机号 | ||||
String mobile = Provider.of<UserInfoNotifier>(context, listen: false)?.getUserInfoModel()?.mobile ?? ''; | |||||
UserInfoModel model = await Provider.of<UserInfoNotifier>(context, listen: false)?.getUserInfoModel(); | |||||
String mobile = model?.mobile ?? ''; | |||||
if (!EmptyUtil.isEmpty(inviteNum) && !EmptyUtil.isEmpty(mobile)) { | if (!EmptyUtil.isEmpty(inviteNum) && !EmptyUtil.isEmpty(mobile)) { | ||||
BlocProvider.of<LoginInviteBloc>(context).add(LoginInviteSubmitEvent(mobile: mobile, num: inviteNum)); | BlocProvider.of<LoginInviteBloc>(context).add(LoginInviteSubmitEvent(mobile: mobile, num: inviteNum)); | ||||
} | } | ||||
@@ -9,6 +9,9 @@ import 'package:zhiying_base_widget/pages/login_page/model/login_model.dart'; | |||||
import 'package:zhiying_comm/util/empty_util.dart'; | import 'package:zhiying_comm/util/empty_util.dart'; | ||||
import 'package:zhiying_comm/zhiying_comm.dart'; | import 'package:zhiying_comm/zhiying_comm.dart'; | ||||
import 'package:cached_network_image/cached_network_image.dart'; | import 'package:cached_network_image/cached_network_image.dart'; | ||||
import 'package:provider/provider.dart'; | |||||
import 'login_page_sk.dart'; | |||||
/// | /// | ||||
/// 登陆页面 | /// 登陆页面 | ||||
@@ -98,7 +101,7 @@ class _LoginPageContainerState extends State<LoginPageContainer> { | |||||
if (state is LoginLoadedState) { | if (state is LoginLoadedState) { | ||||
return _getMainWidget(state.model); | return _getMainWidget(state.model); | ||||
} | } | ||||
return Container(); | |||||
return LoginPageSkeleton(); // 骨架屏幕 | |||||
}, | }, | ||||
); | ); | ||||
} | } | ||||
@@ -0,0 +1,87 @@ | |||||
import 'package:shimmer/shimmer.dart'; | |||||
import 'package:flutter/material.dart'; | |||||
/// | |||||
/// 登陆页面的骨架屏 | |||||
/// | |||||
class LoginPageSkeleton extends StatelessWidget { | |||||
@override | |||||
Widget build(BuildContext context) { | |||||
return Scaffold( | |||||
body: Container( | |||||
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), | |||||
width: double.infinity, | |||||
height: double.infinity, | |||||
child: Column( | |||||
crossAxisAlignment: CrossAxisAlignment.center, | |||||
children: <Widget>[ | |||||
Container( | |||||
height: 230, | |||||
child: Column( | |||||
children: <Widget>[ | |||||
/// logn | |||||
Padding(padding: const EdgeInsets.only(top: 60), child: _logo()), | |||||
/// appName | |||||
Padding(padding: const EdgeInsets.only(top: 12), child: _appName()), | |||||
], | |||||
), | |||||
), | |||||
/// btn | |||||
Padding(padding: const EdgeInsets.only(top: 50, left: 28, right: 28), child: _shimmerWidget(width: 320, height: 40, radius: 20)), | |||||
Padding(padding: const EdgeInsets.only(top: 8, left: 28, right: 28), child: _shimmerWidget(width: 320, height: 40, radius: 20)), | |||||
/// 协议 | |||||
Padding(padding: const EdgeInsets.only(top: 9, left: 28, right: 28), child: _shimmerWidget(width: 250, height: 14, radius: 0)), | |||||
Expanded( | |||||
child: Column( | |||||
mainAxisAlignment: MainAxisAlignment.end, | |||||
children: <Widget>[ | |||||
Padding(padding: const EdgeInsets.only(bottom: 18), child: _shimmerWidget(width: 78, height: 18, radius: 0)), | |||||
], | |||||
), | |||||
), | |||||
], | |||||
), | |||||
), | |||||
); | |||||
} | |||||
Widget _logo() { | |||||
return Shimmer.fromColors( | |||||
baseColor: Colors.grey[300], | |||||
highlightColor: Colors.grey[100], | |||||
child: Container( | |||||
height: 80, | |||||
width: 80, | |||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white), | |||||
), | |||||
); | |||||
} | |||||
Widget _appName() { | |||||
return Shimmer.fromColors( | |||||
baseColor: Colors.grey[300], | |||||
highlightColor: Colors.grey[100], | |||||
child: Container( | |||||
width: 90, | |||||
height: 22.5, | |||||
color: Colors.white, | |||||
), | |||||
); | |||||
} | |||||
Widget _shimmerWidget({double width, double height, double radius = 0}) { | |||||
return Shimmer.fromColors( | |||||
baseColor: Colors.grey[300], | |||||
highlightColor: Colors.grey[100], | |||||
child: Container( | |||||
width: width, | |||||
height: height, | |||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), | |||||
), | |||||
); | |||||
} | |||||
} |
@@ -1,15 +1,15 @@ | |||||
import 'package:flutter/cupertino.dart'; | import 'package:flutter/cupertino.dart'; | ||||
import 'package:zhiying_base_widget/widgets/home/home_banner/model/HomeBannerModel.dart'; | import 'package:zhiying_base_widget/widgets/home/home_banner/model/HomeBannerModel.dart'; | ||||
import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | import 'package:zhiying_comm/util/net_util.dart'; | ||||
class HomeBannerRepository { | class HomeBannerRepository { | ||||
/// 读取缓存数据 | /// 读取缓存数据 | ||||
Future<HomeBannerModel> fetchCacheData({@required int modId}) async { | Future<HomeBannerModel> fetchCacheData({@required int modId}) async { | ||||
var reult = await NetUtil.getRequestCachedData('/api/v1/mod', params: { | |||||
var result = await NetUtil.getRequestCachedData('/api/v1/mod', params: { | |||||
'ids': [modId] | 'ids': [modId] | ||||
}); | }); | ||||
if (!EmptyUtil.isEmpty(reult)) { | |||||
if(NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])){ | |||||
return HomeBannerModel(); | return HomeBannerModel(); | ||||
} | } | ||||
return null; | return null; | ||||
@@ -1,4 +1,5 @@ | |||||
import 'dart:async'; | import 'dart:async'; | ||||
import 'dart:math'; | |||||
import 'package:bloc/bloc.dart'; | import 'package:bloc/bloc.dart'; | ||||
import 'package:flutter/cupertino.dart'; | import 'package:flutter/cupertino.dart'; | ||||
@@ -7,8 +8,7 @@ import 'package:zhiying_comm/util/empty_util.dart'; | |||||
import './bloc.dart'; | import './bloc.dart'; | ||||
class HomeQuickEntryBloc | |||||
extends Bloc<HomeQuickEntryEvent, HomeQuickEntryState> { | |||||
class HomeQuickEntryBloc extends Bloc<HomeQuickEntryEvent, HomeQuickEntryState> { | |||||
HomeQuickEntryRepository repository; | HomeQuickEntryRepository repository; | ||||
HomeQuickEntryBloc({@required this.repository}); | HomeQuickEntryBloc({@required this.repository}); | ||||
@@ -17,8 +17,7 @@ class HomeQuickEntryBloc | |||||
HomeQuickEntryState get initialState => InitialHomeQuickEntryState(); | HomeQuickEntryState get initialState => InitialHomeQuickEntryState(); | ||||
@override | @override | ||||
Stream<HomeQuickEntryState> mapEventToState( | |||||
HomeQuickEntryEvent event) async* { | |||||
Stream<HomeQuickEntryState> mapEventToState(HomeQuickEntryEvent event) async* { | |||||
final currentState = state; | final currentState = state; | ||||
if (event is HomeQuickEntryInitEvent) { | if (event is HomeQuickEntryInitEvent) { | ||||
yield* _mapHomeQuickEntryInitToState(event); | yield* _mapHomeQuickEntryInitToState(event); | ||||
@@ -26,14 +25,13 @@ class HomeQuickEntryBloc | |||||
} | } | ||||
/// 初始化 | /// 初始化 | ||||
Stream<HomeQuickEntryState> _mapHomeQuickEntryInitToState( | |||||
HomeQuickEntryInitEvent event) async* { | |||||
Stream<HomeQuickEntryState> _mapHomeQuickEntryInitToState(HomeQuickEntryInitEvent event) async* { | |||||
/// 获取缓存数据 | /// 获取缓存数据 | ||||
var cached = await repository.fetchCachedData(); | |||||
var cached = await repository.fetchCachedData(event: event); | |||||
if (!EmptyUtil.isEmpty(cached)) { | if (!EmptyUtil.isEmpty(cached)) { | ||||
yield HomeQuickEntryCachedState(); | yield HomeQuickEntryCachedState(); | ||||
} | } | ||||
var result = await repository.fetchData(); | |||||
var result = await repository.fetchData(event: event); | |||||
if (!EmptyUtil.isEmpty(result)) { | if (!EmptyUtil.isEmpty(result)) { | ||||
yield HomeQuickEntryLoadedState(); | yield HomeQuickEntryLoadedState(); | ||||
} else { | } else { | ||||
@@ -8,4 +8,11 @@ abstract class HomeQuickEntryEvent extends Equatable { | |||||
} | } | ||||
/// 初始事件 | /// 初始事件 | ||||
class HomeQuickEntryInitEvent extends HomeQuickEntryEvent {} | |||||
class HomeQuickEntryInitEvent extends HomeQuickEntryEvent { | |||||
final Map<String, dynamic> model; | |||||
const HomeQuickEntryInitEvent({this.model}); | |||||
@override | |||||
List<Object> get props => [this.model]; | |||||
} |
@@ -1,20 +1,23 @@ | |||||
import 'package:flutter/cupertino.dart'; | |||||
import 'package:zhiying_comm/util/net_util.dart'; | import 'package:zhiying_comm/util/net_util.dart'; | ||||
import 'package:zhiying_comm/util/empty_util.dart'; | import 'package:zhiying_comm/util/empty_util.dart'; | ||||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
import 'home_quick_entry_event.dart'; | |||||
class HomeQuickEntryRepository { | class HomeQuickEntryRepository { | ||||
/// 获取数据 | /// 获取数据 | ||||
Future<dynamic> fetchData() async { | |||||
var result = await NetUtil.post('/api/v1/mod', params: {'ids':[7]}, cache: true); | |||||
if(NetUtil.isSuccess(result)){ | |||||
Future<dynamic> fetchData({@required HomeQuickEntryInitEvent event}) async { | |||||
var result = await NetUtil.post('/api/v1/mod', params: {'ids':[event.model['mod_id']]}, cache: true); | |||||
if(NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])){ | |||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
/// 获取缓存数据 | /// 获取缓存数据 | ||||
Future<dynamic> fetchCachedData() async{ | |||||
var result = await NetUtil.getRequestCachedData('/api/v1/mod', params: {'ids':[7]}); | |||||
Future<dynamic> fetchCachedData({@required HomeQuickEntryInitEvent event}) async{ | |||||
var result = await NetUtil.getRequestCachedData('/api/v1/mod', params: {'ids':[event.model['mod_id']]}); | |||||
if(!EmptyUtil.isEmpty(result)){ | if(!EmptyUtil.isEmpty(result)){ | ||||
return result; | return result; | ||||
} | } | ||||
@@ -14,6 +14,6 @@ class HomeQuickEntryCreater extends WidgetCreater { | |||||
@override | @override | ||||
List<Widget> createWidgets(Map<String, dynamic> model) { | List<Widget> createWidgets(Map<String, dynamic> model) { | ||||
return [HomeQuickEntryWidget()]; | |||||
return [HomeQuickEntryWidget(model)]; | |||||
} | } | ||||
} | } |
@@ -8,6 +8,9 @@ import 'package:zhiying_comm/zhiying_comm.dart'; | |||||
/// | /// | ||||
class HomeQuickEntryWidget extends StatelessWidget { | class HomeQuickEntryWidget extends StatelessWidget { | ||||
final Map<String, dynamic> model; | |||||
HomeQuickEntryWidget(this.model); | |||||
var data = []; | var data = []; | ||||
@override | @override | ||||
@@ -6,8 +6,7 @@ import 'package:zhiying_comm/util/net_util.dart'; | |||||
class HomeSlideBannerRepository { | class HomeSlideBannerRepository { | ||||
/// 获取缓存数据 | /// 获取缓存数据 | ||||
Future<List<HomeSlideBannerModelItems>> fetchCachedDate( | |||||
{@required int id}) async { | |||||
Future<List<HomeSlideBannerModelItems>> fetchCachedDate({@required int id}) async { | |||||
var cached = await NetUtil.getRequestCachedData('/api/v1/mod', params: { | var cached = await NetUtil.getRequestCachedData('/api/v1/mod', params: { | ||||
'ids': [id] | 'ids': [id] | ||||
}); | }); | ||||
@@ -22,12 +21,13 @@ class HomeSlideBannerRepository { | |||||
/// 获取数据 | /// 获取数据 | ||||
Future<List<HomeSlideBannerModelItems>> fetchData({@required int id}) async { | Future<List<HomeSlideBannerModelItems>> fetchData({@required int id}) async { | ||||
var params = await NetUtil.post('/api/v1/mod', params: { | |||||
'ids': [id] | |||||
}, cache: true); | |||||
if (!EmptyUtil.isEmpty(params) && NetUtil.isSuccess(params)) { | |||||
HomeSlideBannerModel model = HomeSlideBannerModel.fromJson( | |||||
params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
var params = await NetUtil.post('/api/v1/mod', | |||||
params: { | |||||
'ids': [id] | |||||
}, | |||||
cache: true); | |||||
if (NetUtil.isSuccess(params) && !EmptyUtil.isEmpty(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { | |||||
HomeSlideBannerModel model = HomeSlideBannerModel.fromJson(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); | |||||
if (null != model && !EmptyUtil.isEmpty(model.items)) { | if (null != model && !EmptyUtil.isEmpty(model.items)) { | ||||
return model.items; | return model.items; | ||||
} | } | ||||
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; | |||||
import 'package:flutter_swiper/flutter_swiper.dart'; | import 'package:flutter_swiper/flutter_swiper.dart'; | ||||
import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||
import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; | import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; | ||||
import 'package:cached_network_image/cached_network_image.dart'; | |||||
import 'package:zhiying_comm/zhiying_comm.dart'; | import 'package:zhiying_comm/zhiying_comm.dart'; | ||||
import 'bloc/bloc.dart'; | import 'bloc/bloc.dart'; | ||||
@@ -26,9 +27,7 @@ class _HomeSlideBannerState extends State<HomeSlideBanner> { | |||||
@override | @override | ||||
Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
return BlocProvider<HomeSlideBannerBloc>( | return BlocProvider<HomeSlideBannerBloc>( | ||||
create: (_) => | |||||
HomeSlideBannerBloc(repository: HomeSlideBannerRepository()) | |||||
..add(HomeBannerInitEvent(model: widget?.model)), | |||||
create: (_) => HomeSlideBannerBloc(repository: HomeSlideBannerRepository())..add(HomeBannerInitEvent(model: widget?.model)), | |||||
child: HomeSlideBannerContainer(), | child: HomeSlideBannerContainer(), | ||||
); | ); | ||||
} | } | ||||
@@ -36,8 +35,7 @@ class _HomeSlideBannerState extends State<HomeSlideBanner> { | |||||
class HomeSlideBannerContainer extends StatefulWidget { | class HomeSlideBannerContainer extends StatefulWidget { | ||||
@override | @override | ||||
_HomeSlideBannerContainerState createState() => | |||||
_HomeSlideBannerContainerState(); | |||||
_HomeSlideBannerContainerState createState() => _HomeSlideBannerContainerState(); | |||||
} | } | ||||
class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | ||||
@@ -78,20 +76,26 @@ class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | |||||
Widget _getMainWidget(List<HomeSlideBannerModelItems> datas) { | Widget _getMainWidget(List<HomeSlideBannerModelItems> datas) { | ||||
return Container( | return Container( | ||||
width: double.infinity, | width: double.infinity, | ||||
height: 400.h, | |||||
height: 140, | |||||
child: Swiper( | child: Swiper( | ||||
itemBuilder: (BuildContext context, int index) { | itemBuilder: (BuildContext context, int index) { | ||||
HomeSlideBannerModelItems items = datas[index]; | |||||
return Container( | return Container( | ||||
color: Colors.primaries[index % Colors.primaries.length], | |||||
// color: Colors.primaries[index % Colors.primaries.length], | |||||
width: double.infinity, | |||||
child: CachedNetworkImage( | |||||
imageUrl: items?.img?? '', | |||||
fit: BoxFit.cover, | |||||
), | |||||
); | ); | ||||
}, | }, | ||||
itemCount: datas?.length ?? 0, | itemCount: datas?.length ?? 0, | ||||
loop: true, | |||||
onTap: (index) => _itemOnClick(datas[index]), | onTap: (index) => _itemOnClick(datas[index]), | ||||
pagination: _SwiperCustomPagination(datas?.length ?? 0), | pagination: _SwiperCustomPagination(datas?.length ?? 0), | ||||
onIndexChanged: (index) { | onIndexChanged: (index) { | ||||
print('切换下一页'); | print('切换下一页'); | ||||
Provider.of<MainPageBgNotifier>(context, listen: false) | |||||
.switchBg(Container( | |||||
Provider.of<MainPageBgNotifier>(context, listen: false).switchBg(Container( | |||||
width: double.infinity, | width: double.infinity, | ||||
height: 200, | height: 200, | ||||
color: Colors.primaries[(index + 2) % Colors.primaries.length], | color: Colors.primaries[(index + 2) % Colors.primaries.length], | ||||
@@ -101,15 +105,14 @@ class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | |||||
); | ); | ||||
} | } | ||||
// 自定义进度条 | |||||
/// 自定义进度条 | |||||
SwiperPlugin _SwiperCustomPagination(int pageCount) { | SwiperPlugin _SwiperCustomPagination(int pageCount) { | ||||
List list = []; | List list = []; | ||||
for (int i = 0; i < pageCount; i++) { | for (int i = 0; i < pageCount; i++) { | ||||
list.add(i); | list.add(i); | ||||
} | } | ||||
return SwiperCustomPagination( | |||||
builder: (BuildContext context, SwiperPluginConfig config) { | |||||
return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { | |||||
return Align( | return Align( | ||||
alignment: Alignment(0.0, 0.9), | alignment: Alignment(0.0, 0.9), | ||||
child: Row( | child: Row( | ||||
@@ -118,13 +121,10 @@ class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | |||||
children: list.map((index) { | children: list.map((index) { | ||||
var borderRadius; | var borderRadius; | ||||
if (index == 0) { | if (index == 0) { | ||||
borderRadius = BorderRadius.only( | |||||
topLeft: Radius.circular(2), bottomLeft: Radius.circular(2)); | |||||
borderRadius = BorderRadius.only(topLeft: Radius.circular(2), bottomLeft: Radius.circular(2)); | |||||
} | } | ||||
if (index == list.length - 1) { | if (index == list.length - 1) { | ||||
borderRadius = BorderRadius.only( | |||||
topRight: Radius.circular(2), | |||||
bottomRight: Radius.circular(2)); | |||||
borderRadius = BorderRadius.only(topRight: Radius.circular(2), bottomRight: Radius.circular(2)); | |||||
} | } | ||||
if (index == config.activeIndex) { | if (index == config.activeIndex) { | ||||
@@ -134,11 +134,7 @@ class _HomeSlideBannerContainerState extends State<HomeSlideBannerContainer> { | |||||
return Container( | return Container( | ||||
height: 4, | height: 4, | ||||
width: 25, | width: 25, | ||||
decoration: BoxDecoration( | |||||
borderRadius: borderRadius, | |||||
color: index == config.activeIndex | |||||
? HexColor.fromHex('#FF4242') | |||||
: HexColor.fromHex('#FFFFFF')), | |||||
decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex('#FF4242') : HexColor.fromHex('#FFFFFF')), | |||||
); | ); | ||||
}).toList(), | }).toList(), | ||||
), | ), | ||||
@@ -13,11 +13,11 @@ dependencies: | |||||
shimmer: ^1.1.1 | shimmer: ^1.1.1 | ||||
flutter_swiper : ^1.1.6 | flutter_swiper : ^1.1.6 | ||||
provider: ^4.0.0 | |||||
bloc: ^4.0.0 | bloc: ^4.0.0 | ||||
flutter_bloc: ^4.0.1 | flutter_bloc: ^4.0.1 | ||||
event_bus: ^1.1.1 | event_bus: ^1.1.1 | ||||
pull_to_refresh: ^1.6.1 | pull_to_refresh: ^1.6.1 | ||||
dev_dependencies: | dev_dependencies: | ||||
flutter_test: | flutter_test: | ||||
@@ -25,7 +25,7 @@ dev_dependencies: | |||||
zhiying_comm: | zhiying_comm: | ||||
path: ../zhiying_comm | path: ../zhiying_comm | ||||
json_serializable: ^3.3.0 | json_serializable: ^3.3.0 | ||||
build_runner: ^1.10.0 | build_runner: ^1.10.0 | ||||