@@ -0,0 +1 @@ | |||
{"sdk":"2.7.2 (Mon Mar 23 22:11:27 2020 +0100) on \"macos_x64\"","analyzer":"/Users/fnuser/.pub-cache/hosted/pub.flutter-io.cn/analyzer-0.39.14","build_resolvers":"/Users/fnuser/.pub-cache/hosted/pub.flutter-io.cn/build_resolvers-1.3.11"} |
@@ -680,7 +680,7 @@ | |||
"languageVersion": "2.1" | |||
} | |||
], | |||
"generated": "2020-09-07T03:45:53.992384Z", | |||
"generated": "2020-09-08T07:00:40.458603Z", | |||
"generator": "pub", | |||
"generatorVersion": "2.7.2" | |||
} |
@@ -0,0 +1,5 @@ | |||
<component name="ProjectCodeStyleConfiguration"> | |||
<state> | |||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" /> | |||
</state> | |||
</component> |
@@ -0,0 +1,13 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="SaveActionSettings"> | |||
<option name="actions"> | |||
<set> | |||
<option value="activate" /> | |||
<option value="organizeImports" /> | |||
<option value="reformat" /> | |||
</set> | |||
</option> | |||
<option name="configurationPath" value="" /> | |||
</component> | |||
</project> |
@@ -2,7 +2,6 @@ import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/main_page_bloc.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/main_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/main_page_sk.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
@@ -25,11 +24,13 @@ class _MainPageState extends State<MainPage> { | |||
Widget build(BuildContext context) { | |||
return Scaffold( | |||
backgroundColor: Color(0xfff9f9f9), | |||
body: ChangeNotifierProvider( | |||
create: (context) => MainPageNotifier(), | |||
child: BlocProvider<MainPageBloc>( | |||
bloc: MainPageBloc(), | |||
child: _MainPageContainer(widget.data), | |||
body: SafeArea( | |||
child: ChangeNotifierProvider( | |||
create: (context) => MainPageNotifier(), | |||
child: BlocProvider<MainPageBloc>( | |||
bloc: MainPageBloc(), | |||
child: _MainPageContainer(widget.data), | |||
), | |||
), | |||
), | |||
); | |||
@@ -46,29 +47,16 @@ class _MainPageContainer extends StatefulWidget { | |||
} | |||
class _MainPageContainerState extends State<_MainPageContainer> { | |||
WidgetType _type = WidgetType.normal; | |||
bool _isEnded = false; | |||
ScrollController _controller = ScrollController(); | |||
MainPageBloc _bloc; | |||
List<String> items = ["1", "2", "3", "4", "5", "6", "7", "8"]; | |||
RefreshController _refreshController = | |||
RefreshController(initialRefresh: false); | |||
void _onRefresh() async { | |||
// monitor network fetch | |||
await Future.delayed(Duration(milliseconds: 1000)); | |||
// if failed,use refreshFailed() | |||
_refreshController.refreshCompleted(); | |||
} | |||
void _onLoading() async { | |||
// monitor network fetch | |||
await Future.delayed(Duration(milliseconds: 1000)); | |||
// if failed,use loadFailed(),if no data return,use LoadNodata() | |||
items.add((items.length + 1).toString()); | |||
if (mounted) setState(() {}); | |||
_refreshController.loadComplete(); | |||
// await Future.delayed(Duration(milliseconds: 1000)); | |||
// if (mounted) setState(() {}); | |||
// _refreshController.loadComplete(); | |||
} | |||
@override | |||
@@ -80,7 +68,6 @@ class _MainPageContainerState extends State<_MainPageContainer> { | |||
@override | |||
void initState() { | |||
_bloc = BlocProvider.of<MainPageBloc>(context); | |||
_reload(); | |||
_controller.addListener(() { | |||
// print('${_controller.offset} ${_controller.position.maxScrollExtent}'); | |||
if (_controller.offset >= _controller.position.maxScrollExtent && | |||
@@ -97,79 +84,16 @@ class _MainPageContainerState extends State<_MainPageContainer> { | |||
super.initState(); | |||
} | |||
void _reload() { | |||
setState(() { | |||
_type = WidgetType.skeleton; | |||
}); | |||
WidgetModel model = WidgetModel.fromJson(widget.data); | |||
if ((model?.components?.length ?? 0) > 0) { | |||
_bloc.loadData(model.components.map((item) => item.modId).toList()); | |||
} | |||
// Future.delayed(Duration(seconds: 3), () { | |||
// setState(() { | |||
// _type = WidgetType.normal; | |||
// }); | |||
// }); | |||
} | |||
List<Widget> _createContent(BuildContext context) { | |||
List<Widget> list = List(); | |||
list.add(SliverToBoxAdapter( | |||
child: RaisedButton( | |||
onPressed: () { | |||
Provider.of<MainPageNotifier>(context, listen: false).loadMore(); | |||
_reload(); | |||
}, | |||
child: Text('重置'), | |||
), | |||
)); | |||
list.addAll(WidgetFactory.create('index_carousel', | |||
type: _type, | |||
isSliver: true, | |||
model: Map<String, dynamic>.from({'aaaaa': 'bbbb'}))); | |||
list.addAll(WidgetFactory.create('home_quick_entry', type: _type, isSliver: true)); | |||
list.addAll(WidgetFactory.create('index_recommend_list', | |||
type: _type, isSliver: true)); | |||
return list; | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return StreamBuilder<List<List<Map<String, dynamic>>>>( | |||
return StreamBuilder<Map<String, dynamic>>( | |||
stream: _bloc.outData, | |||
builder: (BuildContext context, AsyncSnapshot snapshot) { | |||
if (snapshot.data == null) { | |||
return MainPageSkeleton(); | |||
} | |||
_type = WidgetType.normal; | |||
return SmartRefresher( | |||
enablePullDown: true, | |||
enablePullUp: true, | |||
enablePullUp: false, | |||
header: WaterDropHeader(), | |||
footer: CustomFooter( | |||
builder: (BuildContext context, LoadStatus mode) { | |||
Widget body; | |||
if (mode == LoadStatus.idle) { | |||
body = Text("上拉加载"); | |||
} else if (mode == LoadStatus.loading) { | |||
body = CupertinoActivityIndicator(); | |||
} else if (mode == LoadStatus.failed) { | |||
body = Text("加载失败!点击重试!"); | |||
} else if (mode == LoadStatus.canLoading) { | |||
body = Text("松手,加载更多!"); | |||
} else { | |||
body = Text("没有更多数据了!"); | |||
} | |||
return Container( | |||
height: 55.0, | |||
child: Center(child: body), | |||
); | |||
}, | |||
), | |||
controller: _refreshController, | |||
onRefresh: _onRefresh, | |||
onLoading: _onLoading, | |||
child: CustomScrollView( | |||
controller: _controller, | |||
@@ -179,4 +103,26 @@ class _MainPageContainerState extends State<_MainPageContainer> { | |||
}, | |||
); | |||
} | |||
List<Widget> _createContent(BuildContext context) { | |||
WidgetModel model = WidgetModel.fromJson(widget.data); | |||
List<Widget> list = List(); | |||
for (int i = 0; i < model.components.length; i++) { | |||
WidgetModel item = model.components[i]; | |||
Map<String, dynamic> data = null; | |||
if (widget.data.containsKey('components')) { | |||
data = widget.data['components'][i]; | |||
} | |||
list.addAll(WidgetFactory.create( | |||
item.modName, | |||
type: WidgetType.skeleton, | |||
isSliver: true, | |||
model: data, | |||
)); | |||
} | |||
model.components.forEach((item) {}); | |||
return list; | |||
} | |||
} |
@@ -4,12 +4,11 @@ import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
class MainPageBloc extends BlocBase { | |||
List<List<Map<String, dynamic>>> _mains = List(); | |||
Map<String, dynamic> _mains = Map(); | |||
StreamController<List<List<Map<String, dynamic>>>> _mainController = | |||
StreamController<List<List<Map<String, dynamic>>>>(); | |||
Stream<List<List<Map<String, dynamic>>>> get outData => | |||
_mainController.stream; | |||
StreamController<Map<String, dynamic>> _mainController = | |||
StreamController<Map<String, dynamic>>(); | |||
Stream<Map<String, dynamic>> get outData => _mainController.stream; | |||
@override | |||
void dispose() { | |||
@@ -18,20 +17,12 @@ class MainPageBloc extends BlocBase { | |||
} | |||
void loadData(List<int> ids) { | |||
NetUtil.request('/api/v1/mod', | |||
method: NetMethod.POST, | |||
params: Map<String, dynamic>.from({'ids': ids}), onSuccess: (data) { | |||
Map<String, dynamic> datas = data; | |||
List<List<Map<String, dynamic>>> result = List(); | |||
for (int id in ids) { | |||
if (datas.containsKey(id.toString())) { | |||
result.add(List<Map<String, dynamic>>.from(datas[id.toString()])); | |||
} else { | |||
result.add(List<Map<String, dynamic>>()); | |||
} | |||
} | |||
_mains = result; | |||
_mainController.add(_mains); | |||
NetUtil.request('/api/v1/config.json', onSuccess: (data) { | |||
List list = data; | |||
List<Map<String, dynamic>> tabs = list.map((item) { | |||
return Map<String, dynamic>.from(item); | |||
}).toList(); | |||
_mainController.add(tabs.first); | |||
}); | |||
} | |||
} |
@@ -1,33 +0,0 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
// ignore: must_be_immutable | |||
class MainPageSkeleton extends StatelessWidget { | |||
ScrollController _controller; | |||
MainPageSkeleton({Key key}) : super(key: key) { | |||
_controller = ScrollController(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return SafeArea( | |||
child: CustomScrollView( | |||
controller: _controller, | |||
slivers: _createContent(context), | |||
), | |||
); | |||
} | |||
List<Widget> _createContent(BuildContext context) { | |||
List<Widget> list = List(); | |||
list.addAll(WidgetFactory.create('index_carousel', | |||
type: WidgetType.skeleton, | |||
isSliver: true, | |||
model: Map<String, dynamic>.from({'aaaaa': 'bbbb'}))); | |||
list.addAll(WidgetFactory.create('index_recommend_list', | |||
type: WidgetType.skeleton, isSliver: true)); | |||
return list; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
import 'dart:async'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_style_model.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class HomeGoodsBloc extends BlocBase { | |||
List<HomeGoodsModel> _goods = List(); | |||
String storeName = ''; | |||
HomeGoodsStyleModel style; | |||
String _provider = ''; | |||
int _page = 1; | |||
StreamController<List<HomeGoodsModel>> _goodsController = | |||
StreamController<List<HomeGoodsModel>>(); | |||
Stream<List<HomeGoodsModel>> get outData => _goodsController.stream; | |||
@override | |||
void dispose() { | |||
_goodsController.close(); | |||
_goodsController = null; | |||
} | |||
void loadMore(String provider) { | |||
if (provider != _provider) { | |||
_page = 1; | |||
} | |||
_provider = provider; | |||
if (_page == 1) { | |||
_goods.clear(); | |||
} | |||
NetUtil.request( | |||
'/api/v1/rec/${provider.toString()}?page=${_page.toString()}', | |||
method: NetMethod.GET, | |||
onCache: (data) {}, onSuccess: (data) { | |||
Map<String, dynamic> json = Map<String, dynamic>.from(data); | |||
if (json.containsKey('good')) { | |||
List<dynamic> list = json['good']; | |||
List<HomeGoodsModel> goods = list.map((item) { | |||
return HomeGoodsModel.fromJson(Map<String, dynamic>.from(item)); | |||
}).toList(); | |||
if (goods.length > 0) { | |||
_page++; | |||
} | |||
_goods.addAll(goods); | |||
} | |||
if (json.containsKey('style')) { | |||
style = HomeGoodsStyleModel.fromJson( | |||
Map<String, dynamic>.from(json['style'])); | |||
} | |||
if (json.containsKey('provider_name')) { | |||
storeName = json['provider_name'].toString(); | |||
} | |||
_goodsController.add(_goods); | |||
}); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
import 'dart:async'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_header_model.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
class HomeGoodsHeaderBloc extends BlocBase { | |||
List<HomeGoodsHeaderModel> _tabs = List(); | |||
StreamController<List<HomeGoodsHeaderModel>> _tabController = | |||
StreamController<List<HomeGoodsHeaderModel>>(); | |||
Stream<List<HomeGoodsHeaderModel>> get outData => _tabController.stream; | |||
@override | |||
void dispose() { | |||
_tabController.close(); | |||
_tabController = null; | |||
} | |||
void loadData(int id) { | |||
NetUtil.request('/api/v1/mod', | |||
method: NetMethod.POST, | |||
params: Map<String, dynamic>.from({ | |||
'ids': [id] | |||
}), | |||
onCache: (data) {}, onSuccess: (data) { | |||
String key = id.toString(); | |||
Map<String, dynamic> json = Map<String, dynamic>.from(data); | |||
if (json.containsKey(key)) { | |||
List<dynamic> list = json[key]; | |||
_tabs = list.map((item) { | |||
return HomeGoodsHeaderModel.fromJson(Map<String, dynamic>.from(item)); | |||
}).toList(); | |||
} | |||
_tabController.add(_tabs); | |||
}); | |||
} | |||
} |
@@ -1,10 +1,20 @@ | |||
import 'package:event_bus/event_bus.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:provider/provider.dart'; | |||
import 'package:zhiying_base_widget/pages/main_page/main_page_notifier.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/bloc/home_goods_bloc.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/home_goods_creater.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/home_goods_item.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/skeleton/home_goods_sk.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
class HomeGoods extends StatefulWidget { | |||
HomeGoods({Key key}) : super(key: key); | |||
final Map<String, dynamic> data; | |||
final EventBus eventBus; | |||
HomeGoods(this.data, {Key key, this.eventBus}) : super(key: key); | |||
@override | |||
_HomeGoodsState createState() => _HomeGoodsState(); | |||
@@ -16,13 +26,48 @@ class _HomeGoodsState extends State<HomeGoods> { | |||
super.initState(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<HomeGoodsBloc>( | |||
bloc: HomeGoodsBloc(), | |||
child: _HomeGoodsContainer(widget.data, widget.eventBus), | |||
); | |||
} | |||
} | |||
class _HomeGoodsContainer extends StatefulWidget { | |||
final Map<String, dynamic> data; | |||
final EventBus eventBus; | |||
_HomeGoodsContainer(this.data, this.eventBus, {Key key}) : super(key: key); | |||
@override | |||
_HomeGoodsContainerState createState() => _HomeGoodsContainerState(); | |||
} | |||
class _HomeGoodsContainerState extends State<_HomeGoodsContainer> { | |||
HomeGoodsBloc _bloc; | |||
String _provider = ''; | |||
@override | |||
void initState() { | |||
_bloc = BlocProvider.of<HomeGoodsBloc>(context); | |||
widget.eventBus.on<HomeGoodsHeaderEvent>().listen((data) { | |||
_provider = data.provider; | |||
_bloc.loadMore(_provider); | |||
}); | |||
super.initState(); | |||
} | |||
@override | |||
void didChangeDependencies() { | |||
super.didChangeDependencies(); | |||
print('didChangeDependencies'); | |||
bool isNeedLoadMore = Provider.of<MainPageNotifier>(context).scrollEnd; | |||
if (isNeedLoadMore) { | |||
if (isNeedLoadMore && _provider != '') { | |||
print('HomeGoods loadmore...'); | |||
_bloc.loadMore(_provider); | |||
} | |||
} | |||
@@ -30,19 +75,28 @@ class _HomeGoodsState extends State<HomeGoods> { | |||
Widget build(BuildContext context) { | |||
return Container( | |||
width: double.infinity, | |||
child: ListView.builder( | |||
shrinkWrap: true, | |||
physics: NeverScrollableScrollPhysics(), | |||
itemCount: 20, | |||
itemBuilder: (context, index) { | |||
return Container( | |||
width: double.infinity, | |||
height: 70, | |||
color: Colors.primaries[index % Colors.primaries.length], | |||
child: Text( | |||
Provider.of<MainPageNotifier>(context).scrollEnd.toString()), | |||
); | |||
}), | |||
child: StreamBuilder<List<HomeGoodsModel>>( | |||
stream: _bloc.outData, | |||
builder: (BuildContext context, AsyncSnapshot snapshot) { | |||
if (snapshot.data == null) { | |||
return HomeGoodsSkeleton(); | |||
} | |||
List<HomeGoodsModel> goods = snapshot.data; | |||
return ListView.builder( | |||
shrinkWrap: true, | |||
physics: NeverScrollableScrollPhysics(), | |||
itemCount: goods.length, | |||
itemBuilder: (context, index) { | |||
return HomeGoodsItem( | |||
goods[index], | |||
_bloc.style, | |||
data: widget.data, | |||
shop: _bloc.storeName, | |||
); | |||
}); | |||
}, | |||
), | |||
); | |||
} | |||
} |
@@ -1,45 +1,42 @@ | |||
import 'package:event_bus/event_bus.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/bloc/home_goods_header_bloc.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/home_goods.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/home_goods_header.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/skeleton/home_goods_header_sk.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/skeleton/home_goods_sk.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
import 'package:event_bus/event_bus.dart'; | |||
class GoodsListCreater extends WidgetCreater { | |||
final EventBus _eventBus = EventBus(); | |||
@override | |||
List<Widget> createWidgets(Map<String, dynamic> model) { | |||
EventBus eventBus = EventBus(); | |||
return [ | |||
SliverPersistentHeader( | |||
pinned: true, | |||
floating: false, | |||
delegate: HomeGoodsDelegate(), | |||
), | |||
SliverToBoxAdapter( | |||
child: HomeGoods(), | |||
), | |||
]; | |||
} | |||
print('创建商品列表'); | |||
Widget _widget = BlocProvider<HomeGoodsHeaderBloc>( | |||
bloc: HomeGoodsHeaderBloc(), | |||
child: HomeGoodsHeader(model, _eventBus), | |||
); | |||
@override | |||
List<Widget> createSkeleton(Map<String, dynamic> model) { | |||
return [ | |||
SliverPersistentHeader( | |||
pinned: true, | |||
floating: false, | |||
delegate: HomeGoodsHeaderSkeletonDelegate(), | |||
), | |||
pinned: true, | |||
floating: false, | |||
delegate: HomeGoodsHeaderDelegate(_widget), | |||
), | |||
SliverToBoxAdapter( | |||
child: HomeGoodsSkeleton(), | |||
), | |||
child: HomeGoods(model, eventBus: _eventBus), | |||
), | |||
]; | |||
} | |||
@override | |||
bool isSliverChild() { | |||
return true; | |||
} | |||
} | |||
class HomeGoodsHeaderEvent { | |||
String provider; | |||
HomeGoodsHeaderEvent(this.provider); | |||
} |
@@ -1,27 +1,22 @@ | |||
import 'package:event_bus/event_bus.dart'; | |||
import 'package:flutter/cupertino.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/bloc/home_goods_header_bloc.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/home_goods_creater.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_header_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/skeleton/home_goods_header_sk.dart'; | |||
import 'package:zhiying_comm/util/base_bloc.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
class HomeGoodsDelegate extends SliverPersistentHeaderDelegate { | |||
class HomeGoodsHeaderDelegate extends SliverPersistentHeaderDelegate { | |||
final Widget child; | |||
HomeGoodsHeaderDelegate(this.child) : super(); | |||
@override | |||
Widget build( | |||
BuildContext context, double shrinkOffset, bool overlapsContent) { | |||
// print(shrinkOffset.toString()); | |||
return Container( | |||
color: Colors.red, | |||
alignment: Alignment.center, | |||
child: TabBar( | |||
controller: TabController(initialIndex: 0, length: 6, vsync: ScrollableState()), | |||
isScrollable: true, | |||
tabs: <Widget>[ | |||
Tab(icon: Icon(Icons.local_florist)), | |||
Tab(icon: Icon(Icons.change_history)), | |||
Tab(icon: Icon(Icons.directions_bike)), | |||
Tab(icon: Icon(Icons.local_florist)), | |||
Tab(icon: Icon(Icons.change_history)), | |||
Tab(icon: Icon(Icons.directions_bike)), | |||
], | |||
), | |||
); | |||
return child; | |||
} | |||
@override | |||
@@ -34,3 +29,167 @@ class HomeGoodsDelegate extends SliverPersistentHeaderDelegate { | |||
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => | |||
false; // 如果内容需要更新,设置为true | |||
} | |||
class HomeGoodsHeader extends StatelessWidget { | |||
final Map<String, dynamic> model; | |||
final EventBus eventBus; | |||
const HomeGoodsHeader( | |||
this.model, | |||
this.eventBus, { | |||
Key key, | |||
}) : super(key: key); | |||
@override | |||
Widget build(BuildContext context) { | |||
return BlocProvider<HomeGoodsHeaderBloc>( | |||
bloc: HomeGoodsHeaderBloc(), | |||
child: _HomeGoodsHeader(model, eventBus), | |||
); | |||
} | |||
} | |||
class _HomeGoodsHeader extends StatefulWidget { | |||
final Map<String, dynamic> model; | |||
final EventBus eventBus; | |||
_HomeGoodsHeader(this.model, this.eventBus, {Key key}) : super(key: key); | |||
@override | |||
_HomeGoodsHeaderState createState() => _HomeGoodsHeaderState(); | |||
} | |||
class _HomeGoodsHeaderState extends State<_HomeGoodsHeader> { | |||
HomeGoodsHeaderBloc _bloc; | |||
@override | |||
void dispose() { | |||
_bloc.dispose(); | |||
super.dispose(); | |||
} | |||
@override | |||
void initState() { | |||
_bloc = BlocProvider.of<HomeGoodsHeaderBloc>(context); | |||
_bloc.loadData(widget.model['mod_id']); | |||
super.initState(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return StreamBuilder<List<HomeGoodsHeaderModel>>( | |||
stream: _bloc.outData, | |||
builder: (BuildContext context, AsyncSnapshot snapshot) { | |||
if (snapshot.data == null) { | |||
return HomeGoodsHeaderSkeleton(); | |||
} | |||
return HomeGoodsHeaderContainer(snapshot.data, widget.eventBus); | |||
}); | |||
} | |||
} | |||
class HomeGoodsHeaderContainer extends StatefulWidget { | |||
final List<HomeGoodsHeaderModel> headers; | |||
final EventBus eventBus; | |||
HomeGoodsHeaderContainer(this.headers, this.eventBus, {Key key}) | |||
: super(key: key); | |||
@override | |||
_HomeGoodsHeaderContainerState createState() => | |||
_HomeGoodsHeaderContainerState(); | |||
} | |||
class _HomeGoodsHeaderContainerState extends State<HomeGoodsHeaderContainer> | |||
with TickerProviderStateMixin { | |||
List<Widget> _widgets = List(); | |||
TabController _tabController; | |||
int _currentIndex = 0; | |||
@override | |||
void initState() { | |||
_tabController = TabController(length: widget.headers.length, vsync: this); | |||
if (widget.headers.first != null) { | |||
widget.eventBus.fire(HomeGoodsHeaderEvent(widget.headers.first.modName)); | |||
} | |||
super.initState(); | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
_widgets.clear(); | |||
for (int index = 0; index < widget.headers.length; index++) { | |||
_widgets.add(_HomeGoodsHeaderItem( | |||
widget.headers[index], | |||
isSelected: index == _currentIndex, | |||
)); | |||
} | |||
return Container( | |||
width: double.infinity, | |||
height: double.infinity, | |||
color: Color(0xfff9f9f9), | |||
child: TabBar( | |||
indicatorColor: Colors.transparent, | |||
controller: _tabController, | |||
isScrollable: true, | |||
tabs: _widgets, | |||
onTap: (index) { | |||
widget.eventBus | |||
.fire(HomeGoodsHeaderEvent(widget.headers[index].modName)); | |||
setState(() { | |||
_currentIndex = index; | |||
}); | |||
}, | |||
), | |||
); | |||
} | |||
} | |||
class _HomeGoodsHeaderItem extends StatelessWidget { | |||
final bool isSelected; | |||
final HomeGoodsHeaderModel model; | |||
const _HomeGoodsHeaderItem(this.model, {Key key, this.isSelected = false}) | |||
: super(key: key); | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container( | |||
// width: 60, | |||
// height: 54, | |||
child: Center( | |||
child: Column( | |||
mainAxisAlignment: MainAxisAlignment.center, | |||
children: <Widget>[ | |||
Text( | |||
model.title, | |||
style: TextStyle( | |||
color: HexColor.fromHex(model.fontColor), | |||
fontSize: 17, | |||
fontWeight: FontWeight.bold), | |||
), | |||
Container( | |||
margin: EdgeInsets.only(top: 4), | |||
padding: EdgeInsets.only(left: 6, right: 6, top: 2, bottom: 2), | |||
decoration: BoxDecoration( | |||
color: isSelected | |||
? HexColor.fromHex(model.fontColor) | |||
: Colors.transparent, | |||
borderRadius: BorderRadius.circular(18)), | |||
child: Text( | |||
model.subtitle, | |||
style: TextStyle( | |||
fontSize: 10, | |||
color: isSelected ? Colors.white : Color(0xff999999)), | |||
textAlign: TextAlign.center, | |||
), | |||
), | |||
], | |||
), | |||
)); | |||
} | |||
} |
@@ -1,15 +1,254 @@ | |||
import 'dart:convert'; | |||
import 'package:cached_network_image/cached_network_image.dart'; | |||
import 'package:flutter/material.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_model.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_goods/models/home_goods_style_model.dart'; | |||
import 'package:zhiying_comm/zhiying_comm.dart'; | |||
// ignore: must_be_immutable | |||
class HomeGoodsItem extends StatelessWidget { | |||
const HomeGoodsItem({Key key}) : super(key: key); | |||
final HomeGoodsModel goods; | |||
final HomeGoodsStyleModel style; | |||
final String shop; | |||
Map<String, dynamic> data; | |||
HomeGoodsItem(this.goods, this.style, {Key key, this.data, this.shop}) | |||
: super(key: key) { | |||
if (this.data != null && this.data.containsKey('data')) { | |||
String data = this.data['data']; | |||
Map<String, dynamic> json = Map<String, dynamic>.from(jsonDecode(data)); | |||
this.data = json; | |||
} | |||
} | |||
@override | |||
Widget build(BuildContext context) { | |||
return Container( | |||
margin: EdgeInsets.only(left: 12.5, right: 12.5, top: 4, bottom: 4), | |||
padding: EdgeInsets.all(7.5), | |||
decoration: BoxDecoration( | |||
color: Colors.white, | |||
borderRadius: BorderRadius.all(Radius.circular(7.5))), | |||
child: Row( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
Container( | |||
width: 126, | |||
height: 126, | |||
margin: EdgeInsets.only(right: 10), | |||
child: ClipRRect( | |||
borderRadius: BorderRadius.circular(7.5), | |||
child: CachedNetworkImage( | |||
imageUrl: goods.goodImage, | |||
fit: BoxFit.fitHeight, | |||
), | |||
), | |||
), | |||
Expanded( | |||
child: Container( | |||
height: 126, | |||
width: double.infinity, | |||
child: Column( | |||
crossAxisAlignment: CrossAxisAlignment.start, | |||
children: <Widget>[ | |||
Container( | |||
width: double.infinity, | |||
child: _createTitle(), | |||
), | |||
_createShop(), | |||
_createCupone(), | |||
Expanded(child: Container()), | |||
_createBottom(), | |||
], | |||
), | |||
), | |||
) | |||
], | |||
), | |||
); | |||
} | |||
Widget _createTitle() { | |||
return RichText( | |||
maxLines: 2, | |||
overflow: TextOverflow.ellipsis, | |||
text: TextSpan(children: [ | |||
WidgetSpan( | |||
child: shop == null || shop == '' | |||
? Container() | |||
: Container( | |||
padding: | |||
EdgeInsets.only(left: 2, right: 2, top: 3, bottom: 3), | |||
margin: EdgeInsets.only(right: 4), | |||
child: Text( | |||
shop, | |||
style: TextStyle( | |||
fontSize: 9, | |||
height: 1, | |||
color: HexColor.fromHex('#ffffff'), | |||
), | |||
), | |||
decoration: BoxDecoration( | |||
color: Colors.red, | |||
borderRadius: BorderRadius.circular(2.5)), | |||
), | |||
), | |||
TextSpan( | |||
text: goods.goodTitle, | |||
style: TextStyle( | |||
fontSize: 15, | |||
color: HexColor.fromHex('#333333'), | |||
fontWeight: FontWeight.bold), | |||
) | |||
]), | |||
); | |||
} | |||
Widget _createShop() { | |||
return Container( | |||
margin: EdgeInsets.only(top: 5), | |||
width: double.infinity, | |||
child: Row( | |||
children: <Widget>[ | |||
Container( | |||
width: 15, | |||
height: 15, | |||
margin: EdgeInsets.only(right: 2), | |||
child: CachedNetworkImage( | |||
imageUrl: style.shopIcon, | |||
fit: BoxFit.fitHeight, | |||
), | |||
), | |||
Expanded( | |||
child: Text( | |||
goods.shopName, | |||
style: TextStyle( | |||
color: HexColor.fromHex(style.shopNameColor), | |||
fontSize: 11, | |||
fontWeight: FontWeight.w400), | |||
)) | |||
], | |||
), | |||
); | |||
} | |||
Widget _createCupone() { | |||
List<Widget> widgets = List(); | |||
if (goods.coupon != null && goods.coupon != '') { | |||
widgets.add(Container( | |||
margin: EdgeInsets.only(right: 5), | |||
padding: EdgeInsets.only(left: 10, right: 10, top: 2, bottom: 2), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(style.couponBackgroundColor), | |||
borderRadius: BorderRadius.circular(2.5), | |||
), | |||
child: Text( | |||
goods.coupon, | |||
textAlign: TextAlign.center, | |||
maxLines: 1, | |||
style: TextStyle( | |||
height: 1, | |||
fontSize: 11, | |||
color: HexColor.fromHex(style.couponFontColor), | |||
), | |||
), | |||
)); | |||
} | |||
if (goods.commission != null || goods.commission != '') { | |||
widgets.add(Container( | |||
margin: EdgeInsets.only(right: 5), | |||
padding: EdgeInsets.only(left: 10, right: 10, top: 2, bottom: 2), | |||
decoration: BoxDecoration( | |||
color: HexColor.fromHex(style.commissionBackgroundColor), | |||
borderRadius: BorderRadius.circular(2.5), | |||
), | |||
child: Text( | |||
goods.commission, | |||
textAlign: TextAlign.center, | |||
maxLines: 1, | |||
style: TextStyle( | |||
height: 1, | |||
fontSize: 11, | |||
color: HexColor.fromHex(style.commissionFontColor), | |||
), | |||
), | |||
)); | |||
} | |||
return Container( | |||
margin: EdgeInsets.only(top: 6), | |||
child: Row( | |||
children: widgets, | |||
), | |||
); | |||
} | |||
Widget _createBottom() { | |||
return Container( | |||
child: Row( | |||
crossAxisAlignment: CrossAxisAlignment.end, | |||
children: <Widget>[ | |||
_createPrice(), | |||
_createOriginPrice(), | |||
Expanded(child: Container()), | |||
_createSale(), | |||
], | |||
), | |||
); | |||
} | |||
Widget _createPrice() { | |||
return RichText( | |||
maxLines: 1, | |||
overflow: TextOverflow.ellipsis, | |||
text: TextSpan(children: [ | |||
TextSpan( | |||
text: '¥', | |||
style: TextStyle( | |||
fontSize: 10, | |||
color: HexColor.fromHex(style.currentPriceColor), | |||
fontWeight: FontWeight.bold, | |||
fontFamily: 'Din', | |||
package: 'zhiying_base_widget', | |||
), | |||
), | |||
TextSpan( | |||
text: goods.currentPrice, | |||
style: TextStyle( | |||
fontSize: 20, | |||
color: HexColor.fromHex(style.currentPriceColor), | |||
fontWeight: FontWeight.bold, | |||
fontFamily: 'Din', | |||
package: 'zhiying_base_widget', | |||
), | |||
) | |||
]), | |||
); | |||
} | |||
Widget _createOriginPrice() { | |||
return Padding( | |||
padding: EdgeInsets.only(bottom: 2), | |||
child: Text( | |||
'¥${goods.marketPrice}', | |||
style: TextStyle( | |||
fontSize: 11, | |||
height: 1, | |||
color: HexColor.fromHex(style.marketPriceColor), | |||
fontWeight: FontWeight.normal, | |||
fontFamily: 'Din', | |||
package: 'zhiying_base_widget', | |||
decoration: TextDecoration.lineThrough, | |||
), | |||
)); | |||
} | |||
Widget _createSale() { | |||
return Text( | |||
'${goods.inorderCount}', | |||
style: TextStyle(fontSize: 11, color: HexColor.fromHex('#999999')), | |||
); | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
import 'package:json_annotation/json_annotation.dart'; | |||
part 'home_goods_header_model.g.dart'; | |||
@JsonSerializable() | |||
class HomeGoodsHeaderModel extends Object { | |||
@JsonKey(name: 'mod_id') | |||
int modId; | |||
@JsonKey(name: 'mod_pid') | |||
int modPid; | |||
@JsonKey(name: 'mod_name') | |||
String modName; | |||
@JsonKey(name: 'position') | |||
String position; | |||
@JsonKey(name: 'title') | |||
String title; | |||
@JsonKey(name: 'subtitle') | |||
String subtitle; | |||
@JsonKey(name: 'url') | |||
String url; | |||
@JsonKey(name: 'margin') | |||
String margin; | |||
@JsonKey(name: 'aspect_ratio') | |||
String aspectRatio; | |||
@JsonKey(name: 'icon') | |||
String icon; | |||
@JsonKey(name: 'img') | |||
String img; | |||
@JsonKey(name: 'font_color') | |||
String fontColor; | |||
@JsonKey(name: 'bg_img') | |||
String bgImg; | |||
@JsonKey(name: 'bg_color') | |||
String bgColor; | |||
@JsonKey(name: 'bg_color_t') | |||
String bgColorT; | |||
@JsonKey(name: 'badge') | |||
String badge; | |||
@JsonKey(name: 'path') | |||
String path; | |||
@JsonKey(name: 'data') | |||
String data; | |||
@JsonKey(name: 'sort') | |||
int sort; | |||
@JsonKey(name: 'is_global') | |||
int isGlobal; | |||
HomeGoodsHeaderModel( | |||
this.modId, | |||
this.modPid, | |||
this.modName, | |||
this.position, | |||
this.title, | |||
this.subtitle, | |||
this.url, | |||
this.margin, | |||
this.aspectRatio, | |||
this.icon, | |||
this.img, | |||
this.fontColor, | |||
this.bgImg, | |||
this.bgColor, | |||
this.bgColorT, | |||
this.badge, | |||
this.path, | |||
this.data, | |||
this.sort, | |||
this.isGlobal, | |||
); | |||
factory HomeGoodsHeaderModel.fromJson(Map<String, dynamic> srcJson) => | |||
_$HomeGoodsHeaderModelFromJson(srcJson); | |||
Map<String, dynamic> toJson() => _$HomeGoodsHeaderModelToJson(this); | |||
} |
@@ -0,0 +1,57 @@ | |||
// GENERATED CODE - DO NOT MODIFY BY HAND | |||
part of 'home_goods_header_model.dart'; | |||
// ************************************************************************** | |||
// JsonSerializableGenerator | |||
// ************************************************************************** | |||
HomeGoodsHeaderModel _$HomeGoodsHeaderModelFromJson(Map<String, dynamic> json) { | |||
return HomeGoodsHeaderModel( | |||
json['mod_id'] as int, | |||
json['mod_pid'] as int, | |||
json['mod_name'] as String, | |||
json['position'] as String, | |||
json['title'] as String, | |||
json['subtitle'] as String, | |||
json['url'] as String, | |||
json['margin'] as String, | |||
json['aspect_ratio'] as String, | |||
json['icon'] as String, | |||
json['img'] as String, | |||
json['font_color'] as String, | |||
json['bg_img'] as String, | |||
json['bg_color'] as String, | |||
json['bg_color_t'] as String, | |||
json['badge'] as String, | |||
json['path'] as String, | |||
json['data'] as String, | |||
json['sort'] as int, | |||
json['is_global'] as int, | |||
); | |||
} | |||
Map<String, dynamic> _$HomeGoodsHeaderModelToJson( | |||
HomeGoodsHeaderModel instance) => | |||
<String, dynamic>{ | |||
'mod_id': instance.modId, | |||
'mod_pid': instance.modPid, | |||
'mod_name': instance.modName, | |||
'position': instance.position, | |||
'title': instance.title, | |||
'subtitle': instance.subtitle, | |||
'url': instance.url, | |||
'margin': instance.margin, | |||
'aspect_ratio': instance.aspectRatio, | |||
'icon': instance.icon, | |||
'img': instance.img, | |||
'font_color': instance.fontColor, | |||
'bg_img': instance.bgImg, | |||
'bg_color': instance.bgColor, | |||
'bg_color_t': instance.bgColorT, | |||
'badge': instance.badge, | |||
'path': instance.path, | |||
'data': instance.data, | |||
'sort': instance.sort, | |||
'is_global': instance.isGlobal, | |||
}; |
@@ -0,0 +1,54 @@ | |||
import 'package:json_annotation/json_annotation.dart'; | |||
part 'home_goods_model.g.dart'; | |||
@JsonSerializable() | |||
class HomeGoodsModel extends Object { | |||
@JsonKey(name: 'provider_name') | |||
String providerName; | |||
@JsonKey(name: 'good_id') | |||
String goodId; | |||
@JsonKey(name: 'good_image') | |||
String goodImage; | |||
@JsonKey(name: 'good_title') | |||
String goodTitle; | |||
@JsonKey(name: 'shop_name') | |||
String shopName; | |||
@JsonKey(name: 'coupon') | |||
String coupon; | |||
@JsonKey(name: 'commission') | |||
String commission; | |||
@JsonKey(name: 'market_price') | |||
String marketPrice; | |||
@JsonKey(name: 'current_price') | |||
String currentPrice; | |||
@JsonKey(name: 'inorder_count') | |||
String inorderCount; | |||
HomeGoodsModel( | |||
this.providerName, | |||
this.goodId, | |||
this.goodImage, | |||
this.goodTitle, | |||
this.shopName, | |||
this.coupon, | |||
this.commission, | |||
this.marketPrice, | |||
this.currentPrice, | |||
this.inorderCount, | |||
); | |||
factory HomeGoodsModel.fromJson(Map<String, dynamic> srcJson) => | |||
_$HomeGoodsModelFromJson(srcJson); | |||
Map<String, dynamic> toJson() => _$HomeGoodsModelToJson(this); | |||
} |
@@ -0,0 +1,36 @@ | |||
// GENERATED CODE - DO NOT MODIFY BY HAND | |||
part of 'home_goods_model.dart'; | |||
// ************************************************************************** | |||
// JsonSerializableGenerator | |||
// ************************************************************************** | |||
HomeGoodsModel _$HomeGoodsModelFromJson(Map<String, dynamic> json) { | |||
return HomeGoodsModel( | |||
json['provider_name'] as String, | |||
json['good_id'] as String, | |||
json['good_image'] as String, | |||
json['good_title'] as String, | |||
json['shop_name'] as String, | |||
json['coupon'] as String, | |||
json['commission'] as String, | |||
json['market_price'] as String, | |||
json['current_price'] as String, | |||
json['inorder_count'] as String, | |||
); | |||
} | |||
Map<String, dynamic> _$HomeGoodsModelToJson(HomeGoodsModel instance) => | |||
<String, dynamic>{ | |||
'provider_name': instance.providerName, | |||
'good_id': instance.goodId, | |||
'good_image': instance.goodImage, | |||
'good_title': instance.goodTitle, | |||
'shop_name': instance.shopName, | |||
'coupon': instance.coupon, | |||
'commission': instance.commission, | |||
'market_price': instance.marketPrice, | |||
'current_price': instance.currentPrice, | |||
'inorder_count': instance.inorderCount, | |||
}; |
@@ -0,0 +1,54 @@ | |||
import 'package:json_annotation/json_annotation.dart'; | |||
part 'home_goods_style_model.g.dart'; | |||
@JsonSerializable() | |||
class HomeGoodsStyleModel extends Object { | |||
@JsonKey(name: 'provider_name_color') | |||
String providerNameColor; | |||
@JsonKey(name: 'provider_name_background_color') | |||
String providerNameBackgroundColor; | |||
@JsonKey(name: 'shop_name_color') | |||
String shopNameColor; | |||
@JsonKey(name: 'shop_icon') | |||
String shopIcon; | |||
@JsonKey(name: 'coupon_background_color') | |||
String couponBackgroundColor; | |||
@JsonKey(name: 'coupon_font_color') | |||
String couponFontColor; | |||
@JsonKey(name: 'commission_background_color') | |||
String commissionBackgroundColor; | |||
@JsonKey(name: 'commission_font_color') | |||
String commissionFontColor; | |||
@JsonKey(name: 'market_price_color') | |||
String marketPriceColor; | |||
@JsonKey(name: 'current_price_color') | |||
String currentPriceColor; | |||
HomeGoodsStyleModel( | |||
this.providerNameColor, | |||
this.providerNameBackgroundColor, | |||
this.shopNameColor, | |||
this.shopIcon, | |||
this.couponBackgroundColor, | |||
this.couponFontColor, | |||
this.commissionBackgroundColor, | |||
this.commissionFontColor, | |||
this.marketPriceColor, | |||
this.currentPriceColor, | |||
); | |||
factory HomeGoodsStyleModel.fromJson(Map<String, dynamic> srcJson) => | |||
_$HomeGoodsStyleModelFromJson(srcJson); | |||
Map<String, dynamic> toJson() => _$HomeGoodsStyleModelToJson(this); | |||
} |
@@ -0,0 +1,37 @@ | |||
// GENERATED CODE - DO NOT MODIFY BY HAND | |||
part of 'home_goods_style_model.dart'; | |||
// ************************************************************************** | |||
// JsonSerializableGenerator | |||
// ************************************************************************** | |||
HomeGoodsStyleModel _$HomeGoodsStyleModelFromJson(Map<String, dynamic> json) { | |||
return HomeGoodsStyleModel( | |||
json['provider_name_color'] as String, | |||
json['provider_name_background_color'] as String, | |||
json['shop_name_color'] as String, | |||
json['shop_icon'] as String, | |||
json['coupon_background_color'] as String, | |||
json['coupon_font_color'] as String, | |||
json['commission_background_color'] as String, | |||
json['commission_font_color'] as String, | |||
json['market_price_color'] as String, | |||
json['current_price_color'] as String, | |||
); | |||
} | |||
Map<String, dynamic> _$HomeGoodsStyleModelToJson( | |||
HomeGoodsStyleModel instance) => | |||
<String, dynamic>{ | |||
'provider_name_color': instance.providerNameColor, | |||
'provider_name_background_color': instance.providerNameBackgroundColor, | |||
'shop_name_color': instance.shopNameColor, | |||
'shop_icon': instance.shopIcon, | |||
'coupon_background_color': instance.couponBackgroundColor, | |||
'coupon_font_color': instance.couponFontColor, | |||
'commission_background_color': instance.commissionBackgroundColor, | |||
'commission_font_color': instance.commissionFontColor, | |||
'MarketPriceColor': instance.marketPriceColor, | |||
'current_price_color': instance.currentPriceColor, | |||
}; |
@@ -1,13 +1,11 @@ | |||
import 'package:flutter/material.dart'; | |||
import 'package:shimmer/shimmer.dart'; | |||
import 'package:zhiying_base_widget/widgets/home_banner/home_banner_sk.dart'; | |||
class HomeGoodsHeaderSkeletonDelegate extends SliverPersistentHeaderDelegate { | |||
HomeGoodsHeaderSkeletonDelegate(); | |||
class HomeGoodsHeaderSkeleton extends StatelessWidget { | |||
const HomeGoodsHeaderSkeleton({Key key}) : super(key: key); | |||
@override | |||
Widget build( | |||
BuildContext context, double shrinkOffset, bool overlapsContent) { | |||
Widget build(BuildContext context) { | |||
return Container( | |||
width: double.infinity, | |||
height: double.infinity, | |||
@@ -48,14 +46,4 @@ class HomeGoodsHeaderSkeletonDelegate extends SliverPersistentHeaderDelegate { | |||
), | |||
); | |||
} | |||
@override | |||
double get maxExtent => 64.0; | |||
@override | |||
double get minExtent => 64.0; | |||
@override | |||
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => | |||
false; // 如果内容需要更新,设置为true | |||
} |
@@ -25,6 +25,10 @@ dev_dependencies: | |||
zhiying_comm: | |||
path: ../zhiying_comm | |||
json_serializable: ^3.3.0 | |||
build_runner: ^1.10.0 | |||
# For information on the generic Dart part of this file, see the | |||
# following page: https://dart.dev/tools/pub/pubspec | |||
@@ -55,17 +59,13 @@ flutter: | |||
# "family" key with the font family name, and a "fonts" key with a | |||
# list giving the asset and other descriptors for the font. For | |||
# example: | |||
# fonts: | |||
# - family: Schyler | |||
# fonts: | |||
# - asset: fonts/Schyler-Regular.ttf | |||
# - asset: fonts/Schyler-Italic.ttf | |||
# style: italic | |||
# - family: Trajan Pro | |||
# fonts: | |||
# - asset: fonts/TrajanPro.ttf | |||
# - asset: fonts/TrajanPro_Bold.ttf | |||
# weight: 700 | |||
fonts: | |||
- family: Din | |||
fonts: | |||
- asset: assets/fonts/DIN-Bold.otf | |||
- asset: assets/fonts/DIN-Medium.otf | |||
- asset: assets/fonts/DIN-Regular.otf | |||
# | |||
# For details regarding fonts in packages, see | |||
# https://flutter.dev/custom-fonts/#from-packages |