基础组件库
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 

400 行
12 KiB

  1. import 'package:flutter/cupertino.dart';
  2. import 'package:pull_to_refresh/pull_to_refresh.dart';
  3. import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart';
  4. import 'package:zhiying_base_widget/widgets/refresh/refresh_header/refresh_header.dart';
  5. import 'package:zhiying_comm/zhiying_comm.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:provider/provider.dart';
  8. import 'package:zhiying_comm/util/custom_sliver_persistent_header_delegate.dart';
  9. import 'package:flutter_bloc/flutter_bloc.dart';
  10. import 'bloc/custom_page_bloc.dart';
  11. import 'bloc/custom_page_state.dart';
  12. import 'bloc/custom_page_event.dart';
  13. import 'bloc/custom_page_repository.dart';
  14. import 'dart:ui';
  15. ///
  16. /// 通用模块页面
  17. ///
  18. class CustomPage extends StatelessWidget {
  19. final Map<String, dynamic> data;
  20. CustomPage(this.data, {Key key}) : super(key: key);
  21. @override
  22. Widget build(BuildContext context) {
  23. // return NestedScrollDemoPage();
  24. return BlocProvider<CustomPageBloc>(
  25. create: (_) => CustomPageBloc(CustomPageRepository(data: data))..add(CustomPageInitEvent()),
  26. child: _CommonPageContainer(),
  27. // ),
  28. );
  29. }
  30. }
  31. class _CommonPageContainer extends StatefulWidget {
  32. @override
  33. __CommonPageContainerState createState() => __CommonPageContainerState();
  34. }
  35. class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin {
  36. ScrollController _controller;
  37. ScrollController _controller2;
  38. RefreshController _refreshController;
  39. TabController _tabController;
  40. bool _isEnded = false;
  41. /// 回到顶点
  42. void _scrollTop() {
  43. // _controller.jumpTo(0);
  44. _controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.linear);
  45. }
  46. /// 刷新
  47. void _onRefreshEvent() async {
  48. BlocProvider.of<CustomPageBloc>(context).add(CustomPageRefreshEvent());
  49. }
  50. /// 加载更多
  51. void _onLoadEvent() async {}
  52. @override
  53. void initState() {
  54. _controller = ScrollController();
  55. _controller2 = ScrollController();
  56. _refreshController = RefreshController(initialRefresh: false);
  57. _tabController = TabController(length: 10, vsync: this);
  58. super.initState();
  59. }
  60. @override
  61. void dispose() {
  62. _controller?.dispose();
  63. _refreshController?.dispose();
  64. _tabController?.dispose();
  65. _controller2?.dispose();
  66. super.dispose();
  67. }
  68. @override
  69. Widget build(BuildContext context) {
  70. return MediaQuery.removePadding(
  71. context: context,
  72. child: BlocConsumer<CustomPageBloc, CustomPageState>(
  73. listener: (context, state) {},
  74. buildWhen: (prev, current) {
  75. if (current is CustomPageErrorState) {
  76. return false;
  77. }
  78. if (current is CustomPageRefreshSuccessState) {
  79. _refreshController.refreshCompleted(resetFooterState: true);
  80. return false;
  81. }
  82. if (current is CustomPageRefreshErrorState) {
  83. _refreshController.refreshFailed();
  84. return false;
  85. }
  86. return true;
  87. },
  88. builder: (context, state) {
  89. // if (state is CustomPageLoadedState) {
  90. // return _buildMain2Widget();
  91. // }
  92. // if (state is CustomPageInitErrorState) {
  93. // return _buildMain2Widget();
  94. // }
  95. // return _buildMain2Widget();
  96. return _buildMain2Widget();
  97. },
  98. ),
  99. );
  100. }
  101. /// 有数据
  102. Widget _buildMainWidget() {
  103. double top = MediaQueryData.fromWindow(window).padding.top;
  104. return Scaffold(
  105. backgroundColor: HexColor.fromHex('#F9F9F9'),
  106. floatingActionButton: _buildFloatWidget(),
  107. floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100),
  108. body: SmartRefresher(
  109. enablePullDown: true,
  110. enablePullUp: false,
  111. header: RefreshHeader(
  112. offsetY: top,
  113. ),
  114. controller: _refreshController,
  115. onLoading: _onLoadEvent,
  116. onRefresh: _onRefreshEvent,
  117. child: CustomScrollView(
  118. key: UniqueKey(),
  119. controller: _controller,
  120. slivers: [
  121. /// 标题
  122. SliverAppBar(
  123. title: Text('标题'),
  124. centerTitle: true,
  125. pinned: true,
  126. ),
  127. /// 轮播图
  128. _buildSliverBanner(),
  129. /// 搜索
  130. _buildSearch(),
  131. /// TAB BAR
  132. _buildTabbar(),
  133. SliverFillRemaining(
  134. child: TabBarView(
  135. controller: _tabController,
  136. children: List.generate(10, (index) => Container(
  137. height: double.infinity,
  138. width: double.infinity,
  139. color: Colors.white,
  140. child: MediaQuery.removePadding(
  141. context: context,
  142. removeTop: true,
  143. child: CustomScrollView(
  144. controller: _controller2,
  145. key: UniqueKey(),
  146. slivers: <Widget>[
  147. _buildQucikEntry(),
  148. _buildSearch(),
  149. _buildSliverBanner(),
  150. _buildGoodsList(),
  151. ],
  152. ),
  153. ),
  154. )),
  155. ),
  156. )
  157. ],
  158. ),
  159. ),
  160. );
  161. }
  162. /// 有数据2
  163. Widget _buildMain2Widget(){
  164. return Scaffold(
  165. appBar: AppBar(title: Text('标题'),centerTitle: true,),
  166. backgroundColor: HexColor.fromHex('#F9F9F9'),
  167. floatingActionButton: _buildFloatWidget(),
  168. floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100),
  169. body: Column(
  170. children: <Widget>[
  171. Container(height: 40, width: double.infinity, color: Colors.green,),
  172. Container(
  173. height: 40,
  174. width: double.infinity,
  175. color: Colors.red,
  176. child: TabBar(
  177. isScrollable: true,
  178. controller: _tabController,
  179. tabs: List.generate(10, (index) => Text('$index')),
  180. ),
  181. ),
  182. Expanded(
  183. child: TabBarView(
  184. controller: _tabController,
  185. children: List.generate(10, (index) => Container(
  186. height: double.infinity,
  187. width: double.infinity,
  188. color: Colors.white,
  189. child: MediaQuery.removePadding(
  190. context: context,
  191. removeTop: true,
  192. child: CustomScrollView(
  193. controller: _controller,
  194. key: UniqueKey(),
  195. slivers: <Widget>[
  196. _buildQucikEntry(),
  197. _buildSearch(),
  198. _buildSliverBanner(),
  199. _buildSearch(title: '商品列表'),
  200. _buildGoodsList(),
  201. ],
  202. ),
  203. ),
  204. )),
  205. ),
  206. )
  207. ],
  208. ),
  209. );
  210. }
  211. /// tabBar
  212. Widget _buildTabbar(){
  213. return SliverPersistentHeader(
  214. delegate: CustomSliverPersistentHeaderDelegate(
  215. max: 40,
  216. min: 40,
  217. child: Container(
  218. height: double.infinity,
  219. width: double.infinity,
  220. color: Colors.redAccent,
  221. child: TabBar(
  222. isScrollable: true,
  223. controller: _tabController,
  224. tabs: List.generate(10, (index) => Text('$index')),
  225. ),
  226. ),
  227. ),
  228. pinned: true,
  229. );
  230. }
  231. /// 多眼导航
  232. Widget _buildQucikEntry(){
  233. return SliverToBoxAdapter(
  234. child: Container(
  235. alignment: Alignment.center,
  236. child: Text('多眼导航'),
  237. height: 70,
  238. width: double.infinity,
  239. color: Colors.green,
  240. ),
  241. );
  242. }
  243. /// 搜索
  244. Widget _buildSearch({String title}){
  245. return SliverPersistentHeader(
  246. delegate: CustomSliverPersistentHeaderDelegate(
  247. max: 40,
  248. min: 40,
  249. child: Container(
  250. alignment: Alignment.center,
  251. child: Text(title ?? '搜索'),
  252. height: double.infinity,
  253. width: double.infinity,
  254. color: Colors.blueAccent,
  255. ),
  256. ),
  257. pinned: true,
  258. );
  259. }
  260. /// 商品列表
  261. Widget _buildGoodsList(){
  262. return SliverList(
  263. delegate: SliverChildBuilderDelegate((context, index) {
  264. return Container(
  265. height: 50,
  266. width: double.infinity,
  267. color: Colors.green[(index % 9 + 1) * 100],
  268. );
  269. }, childCount: 50));
  270. }
  271. /// 轮播图
  272. Widget _buildSliverBanner(){
  273. return SliverPersistentHeader(
  274. delegate: CustomSliverPersistentHeaderDelegate(
  275. max: 140,
  276. min: 140,
  277. child: Container(
  278. child: Text('轮播图'),
  279. alignment: Alignment.center,
  280. height: double.infinity,
  281. width: double.infinity,
  282. color: Colors.yellowAccent,
  283. ),
  284. ),
  285. pinned: false,
  286. );
  287. }
  288. /// 悬浮按钮
  289. Widget _buildFloatWidget() {
  290. return Visibility(
  291. visible: true,
  292. child: GestureDetector(
  293. onTap: ()=> _scrollTop(),
  294. behavior: HitTestBehavior.opaque,
  295. child: Container(
  296. height: 30,
  297. width: 30,
  298. color: Colors.redAccent,
  299. ),
  300. ),
  301. );
  302. }
  303. /// 骨架图
  304. Widget _buildSkeletonWidget() {
  305. return Scaffold();
  306. }
  307. /// 空数据视图
  308. Widget _buildEmptyWidget() {
  309. return Scaffold(
  310. backgroundColor: HexColor.fromHex('#F9F9F9'),
  311. appBar: AppBar(
  312. brightness: Brightness.light,
  313. backgroundColor: Colors.white,
  314. leading: IconButton(
  315. icon: Icon(
  316. Icons.arrow_back_ios,
  317. size: 22,
  318. color: HexColor.fromHex('#333333'),
  319. ),
  320. onPressed: () => Navigator.maybePop(context),
  321. ),
  322. title: Text(
  323. '爆款',
  324. style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 15, fontWeight: FontWeight.bold),
  325. ),
  326. centerTitle: true,
  327. elevation: 0,
  328. ),
  329. body: Column(
  330. crossAxisAlignment: CrossAxisAlignment.center,
  331. mainAxisAlignment: MainAxisAlignment.center,
  332. children: <Widget>[
  333. Align(
  334. alignment: Alignment.topCenter,
  335. child: EmptyWidget(
  336. tips: '网络似乎开小差了~',
  337. ),
  338. ),
  339. GestureDetector(
  340. onTap: () => _onRefreshEvent(),
  341. behavior: HitTestBehavior.opaque,
  342. child: Container(
  343. alignment: Alignment.center,
  344. decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), border: Border.all(color: HexColor.fromHex('#999999'), width: 0.5), color: Colors.white),
  345. width: 80,
  346. height: 20,
  347. child: Text(
  348. '刷新一下',
  349. style: TextStyle(fontSize: 12, color: HexColor.fromHex('#333333'), fontWeight: FontWeight.bold),
  350. ),
  351. ),
  352. )
  353. ],
  354. ));
  355. }
  356. }
  357. class _CustomFloatingActionButtonLocation extends FloatingActionButtonLocation {
  358. FloatingActionButtonLocation location;
  359. double offsetX; // X方向的偏移量
  360. double offsetY; // Y方向的偏移量
  361. _CustomFloatingActionButtonLocation(this.location, this.offsetX, this.offsetY);
  362. @override
  363. Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) {
  364. Offset offset = location.getOffset(scaffoldGeometry);
  365. return Offset(offset.dx + offsetX, offset.dy + offsetY);
  366. }
  367. }