import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; import 'package:huixiang/generated/l10n.dart'; import 'package:huixiang/retrofit/data/banner.dart'; import 'package:huixiang/retrofit/data/base_data.dart'; import 'package:huixiang/retrofit/data/brand_data.dart'; import 'package:huixiang/retrofit/retrofit_api.dart'; import 'package:huixiang/view_widget/classic_header.dart'; import 'package:huixiang/view_widget/custom_image.dart'; import 'package:huixiang/view_widget/icon_text.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:huixiang/view_widget/loading_view.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:shared_preferences/shared_preferences.dart'; class BrandPage extends StatefulWidget { @override State createState() { return _BrandPage(); } } class _BrandPage extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { ScrollController tabcontroller; // List tabs = [ // S.current.bainianchuanjiao, // S.current.haixiajiemei, // S.current.qianjinmaiwei, // ]; Map images = { S.current.bainianchuanjiao: "assets/image/icon_chili.png", S.current.haixiajiemei: "assets/image/icon_milk_tea.png", S.current.qianjinmaiwei: "assets/image/icon_bread.png", }; GlobalKey chiliGlobalKey = GlobalKey(); GlobalKey milkTeaGlobalKey = GlobalKey(); GlobalKey breadGlobalKey = GlobalKey(); ApiService apiService; BrandData brandData; List bannerData = []; queryHome() async { showCupertinoDialog( context: context, barrierDismissible: true, builder: (context) { return LoadingView(); }); BaseData baseData = await apiService.queryHome().catchError((error) { refreshController.refreshFailed(); }); BaseData banner = await apiService.queryBanner({ "model": { "type": "BRAND_APP" }, }).catchError((error) { refreshController.refreshFailed(); }); bannerData.clear(); bannerData.addAll((banner.data["records"] as List).map((e) => BannerData.fromJson(e)).toList()); if (Navigator.canPop(context)) Navigator.of(context).pop(); if (baseData != null && baseData.isSuccess) { refreshController.refreshCompleted(); brandData = BrandData.fromJson(baseData.data); setState(() {}); } else { refreshController.refreshFailed(); } } @override void initState() { super.initState(); SharedPreferences.getInstance().then((value) => { apiService = ApiService(Dio(), token: value.getString('token')), queryHome(), }); if (tabcontroller == null) tabcontroller = ScrollController(); tabcontroller.addListener(() { RenderBox chiliRenderBox = chiliGlobalKey.currentContext.findRenderObject(); RenderBox milkTeaRenderBox = milkTeaGlobalKey.currentContext.findRenderObject(); RenderBox breadRenderBox = breadGlobalKey.currentContext.findRenderObject(); Offset chiliOffset = chiliRenderBox.localToGlobal(Offset.zero); Offset milkTeaOffset = milkTeaRenderBox.localToGlobal(Offset.zero); Offset breadOffset = breadRenderBox.localToGlobal(Offset.zero); var top = 96.h; if (chiliOffset.dy <= top) { if (!isVisible) { isVisible = true; setState(() {}); } } else { var b = isVisible; isVisible = false; if(b) setState(() {}); selectedIndex = ""; } if (chiliOffset.dy <= top && milkTeaOffset.dy > top) { selectedIndex = (brandData.contents as Map).keys.elementAt(0); setState(() {}); } else if (breadOffset.dy <= top) { selectedIndex = (brandData.contents as Map).keys.elementAt(2); setState(() {}); } else if (milkTeaOffset.dy <= top && breadOffset.dy > top) { selectedIndex = (brandData.contents as Map).keys.elementAt(1); setState(() {}); } }); } String selectedIndex = ""; RefreshController refreshController = RefreshController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( toolbarHeight: 40 - MediaQuery.of(context).padding.top, backgroundColor: Colors.white, elevation: 0, ), body: Container( padding: EdgeInsets.only(bottom: 76.h), child: Stack( children: [ Container( child: SmartRefresher( controller: refreshController, enablePullDown: true, enablePullUp: false, header: MyHeader(), scrollController: tabcontroller, physics: BouncingScrollPhysics(), onRefresh: queryHome, child: Container( child: SingleChildScrollView( physics: NeverScrollableScrollPhysics(), child: Container( color: Color(0xFFF7F7F7), margin: EdgeInsets.only(top: 16), child: Column( children: homeChildItem(), ), ), ), ), ), ), Positioned( child: Visibility( visible: isVisible, child: Container( width: MediaQuery.of(context).size.width, child: Container( height: 52.h, color: Colors.white, padding: EdgeInsets.all(6), alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: (brandData == null || brandData.contents == null) ? [] : (brandData.contents as Map).keys.map((e) => item(e, selectedIndex == e)).toList(), ), ), ), ), top: 0, ), ], ), ), ); } List homeChildItem() { var widgets = [banner(), buildInfo(), buildTab()]; if (brandData == null) return widgets; (brandData.contents as Map).forEach((key, value) { widgets.add(Container( key: key == "百年川椒" ? chiliGlobalKey : key == "前进麦味" ? breadGlobalKey : milkTeaGlobalKey, child: Html( data:value, ), )); }); return widgets; } bool isVisible = false; Widget buildTab() { return Container( // key: tabGlobalKey, height: 52.h, padding: EdgeInsets.all(6), alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: (brandData == null || brandData.contents == null) ? [] : (brandData.contents as Map).keys.map((e) => item(e, selectedIndex == e)).toList(), ), ); } Widget item(text, isSelected) { return GestureDetector( onTap: () { FlexParentData parendData; if (text == S.of(context).bainianchuanjiao) { parendData = chiliGlobalKey.currentContext.findRenderObject().parentData; } else if (text == S.of(context).haixiajiemei) { parendData = milkTeaGlobalKey.currentContext.findRenderObject().parentData; } else if (text == S.of(context).qianjinmaiwei) { parendData = breadGlobalKey.currentContext.findRenderObject().parentData; } double offset = parendData.offset.dy - 52.h + 20; tabcontroller.animateTo(offset, duration: Duration(seconds: 1), curve: Curves.ease); }, child: tabItem(text, selectedIndex == text), ); } Widget tabItem(text, isSelected) { if (isSelected) { return IconText( text, isMax: false, rightImage: images[text], iconSize: 16, iconColor: Colors.red, textStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: Color(0xFF353535)), ); } else { return IconText( text, isMax: false, textStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, color: Color(0xFF353535)), ); } } Widget buildInfo() { return Container( margin: EdgeInsets.only(bottom: 20, top: 16), padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withAlpha(12), offset: Offset(0, 2), blurRadius: 14, spreadRadius: 0) ], ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ ClipOval( child: MImage( brandData == null ? "" : brandData.originAvatar, fit: BoxFit.cover, width: 60, height: 60, errorSrc: "assets/image/default_1.png", fadeSrc: "assets/image/default_1.png", ), clipBehavior: Clip.hardEdge, ), SizedBox( width: 16, ), Expanded( child: Container( height: 60, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text.rich( TextSpan(children: [ TextSpan( text: brandData == null ? "" : brandData.originator, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 14, color: Colors.black), ), TextSpan( text: " 集团创办人", style: TextStyle(fontSize: 10, color: Colors.black), ), ]), textDirection: TextDirection.ltr, ), Text( brandData == null ? "" : brandData.originDesc, overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( fontSize: 12, color: Color(0xFF353535), ), ), ], ), ), flex: 1, ) ], ), SizedBox( height: 40, ), Text( brandData == null ? "" : brandData.company, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.black), ), SizedBox( height: 20, ), Text( brandData == null ? "" : brandData.companyDesc, textAlign: TextAlign.justify, style: TextStyle(fontSize: 12.sp, color: Color(0xFF353535)), ), SizedBox( height: 40, ), Text( "理念", style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.black), ), SizedBox( height: 20, ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: buildIdea(), ), ], ), ); } List buildIdea() { if (brandData == null) return []; var ideas = []; (brandData.ideals as Map).forEach((key, value) { ideas.add(idea(key, value)); }); return ideas; } banner() { return Container( child: AspectRatio( aspectRatio: 2.08, child: Swiper( viewportFraction: 0.88, scale: 0.93, // loop: true, // autoplay: true, // duration: 2500, pagination: SwiperPagination( alignment: Alignment.bottomCenter, builder: DotSwiperPaginationBuilder( size: 8, activeSize: 8, space: 5, activeColor: Colors.black, color: Colors.black.withAlpha(76), ), ), physics: BouncingScrollPhysics(), itemBuilder: (context, position) { return MImage( (bannerData != null && position < bannerData.length) ? bannerData[position].imgUrl : "", fit: BoxFit.cover, radius: BorderRadius.circular(8), errorSrc: "assets/image/default_2_1.png", fadeSrc: "assets/image/default_2_1.png", ); }, itemCount: (bannerData != null && bannerData.length > 0) ? bannerData.length : 1), ), ); } Widget idea(key, value) { return Stack( children: [ MImage( value, width: 71, height: 71, fit: BoxFit.cover, errorSrc: "assets/image/default_1.png", fadeSrc: "assets/image/default_1.png", ), Positioned( bottom: 0, child: Container( width: 71, decoration: BoxDecoration( color: Colors.black.withAlpha(125), borderRadius: BorderRadius.only( bottomRight: Radius.circular(4), bottomLeft: Radius.circular(4), ), ), padding: EdgeInsets.symmetric(vertical: 2), alignment: Alignment.center, child: Text( key, style: TextStyle( color: Colors.white, fontSize: 12.sp, ), ), ), ) ], ); } @override bool get wantKeepAlive => true; }