| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_html/flutter_html.dart';
- import 'package:flutter_swiper/flutter_swiper.dart';
- import 'package:cached_network_image/cached_network_image.dart';
- import 'package:twong/api/index.dart';
- import 'package:twong/utils/index.dart';
- import 'package:twong/config/style.dart';
- import 'package:twong/router/index.dart';
- import 'package:twong/models/index.dart';
- import 'package:twong/widgets/future_widget.dart';
- import 'package:twong/pages/product/product_attr.dart';
- import 'package:twong/widgets/share_bar.dart';
- class ProductDetailsPage extends StatefulWidget {
- final dynamic id;
- ProductDetailsPage (this.id, {Key key}): super(key: key);
- @override
- State<StatefulWidget> createState() {
- return _ProductDetailsPageState();
- }
- }
- class _ProductDetailsPageState extends State<ProductDetailsPage> {
- Details _details;
- IconData _likeIcon = Icons.favorite_border;
- @override
- void initState() {
- super.initState();
- }
- void _onAddCart () {
- showModalBottomSheet(
- context: context,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(
- topLeft: Radius.circular(10.px), topRight: Radius.circular(10.px)
- )),
- builder: (BuildContext context) {
- return ProductAttrPage(_details, isCart: true);
- }
- );
- }
- void _onBuy () {
- showModalBottomSheet(
- context: context,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(
- topLeft: Radius.circular(10.px), topRight: Radius.circular(10.px)
- )),
- builder: (BuildContext context) {
- return ProductAttrPage(_details);
- });
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.white,
- body: FutureWidget(Network.inst.getDetail, _buildBody, data: widget.id),
- bottomNavigationBar: SafeArea(
- child: Container(
- height: 60.px,
- color: Colors.white,
- child: Flex(
- direction: Axis.horizontal,
- children: <Widget>[
- Row(
- children: <Widget>[
- GestureDetector(
- onTap: () {
- Navigator.pushNamed(
- context, RouteNames.message, arguments: false);
- },
- child: Container(
- width: 60.px,
- padding: EdgeInsets.only(
- left: 16.px, right: 12.px, top: 12.px),
- child: Column(
- children: <Widget>[
- Icon(Icons.message),
- Text('客服', style: TextStyle(fontSize: 10.px),)
- ],
- ),
- )
- ),
- GestureDetector(
- onTap: () {
- Navigator.pushNamed(
- context, RouteNames.cart, arguments: true);
- },
- child: Container(
- padding: EdgeInsets.only(
- left: 12.px, right: 12.px, top: 12.px),
- child: Column(
- children: <Widget>[
- Icon(Icons.shopping_cart),
- Text('购物车', style: TextStyle(fontSize: 10.px),)
- ],
- ),
- )
- ),
- ],
- ),
- Expanded(child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: <Widget>[
- Expanded(
- child: Container(
- padding: EdgeInsets.all(6.px),
- child: FlatButton(
- onPressed: _onAddCart,
- color: Colors.black,
- shape: StadiumBorder(),
- child: Text('加入购物车',
- style: TextStyle(color: Colors.white)),
- )
- ),
- ),
- Expanded(
- child: Container(
- padding: EdgeInsets.all(6.px),
- child: FlatButton(
- onPressed: _onBuy,
- color: Colors.red,
- shape: StadiumBorder(),
- child: Text('立即购买',
- style: TextStyle(color: Colors.white)),
- )
- ),
- ),
- ],
- )),
- ],
- ),
- ),
- )
- );
- }
- Widget _buildBody(dynamic data) {
- _details = data;
- return CustomScrollView(
- physics: ClampingScrollPhysics(),
- slivers: <Widget>[
- SliverPersistentHeader(
- pinned: true,
- delegate: SliverCustomHeaderDelegate(
- paddingTop: MediaQuery.of(context).padding.top,
- data: _details, likeClick: _likeClick, likeIcon: _likeIcon),
- ),
- SliverToBoxAdapter(
- child: ProductContent(_details),
- )
- ],
- );
- }
- void _likeClick() async {
- if(_likeIcon == Icons.favorite_border) {
- setState(() {
- _likeIcon = Icons.favorite;
- });
- } else {
- setState(() {
- _likeIcon = Icons.favorite_border;
- });
- }
- }
- }
- class SliverCustomHeaderDelegate extends SliverPersistentHeaderDelegate {
- final double collapsedHeight = 40.px;
- final double expandedHeight = 400.px;
- final double paddingTop;
- final Details data;
- final IconData likeIcon;
- final Function likeClick;
- final Color topColor = DColors.Main;
- int tabIdx = 0;
- String statusBarMode = 'dark';
- final Color tColor = Color.fromARGB(168, DColors.Main.red, DColors.Main.green, DColors.Main.blue);
- SliverCustomHeaderDelegate({this.paddingTop, this.data, this.likeClick, this.likeIcon});
- @override
- double get maxExtent => this.expandedHeight;
- @override
- double get minExtent => this.collapsedHeight + this.paddingTop;
- @override
- bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
- void updateStatusBarBrightness(shrinkOffset) {
- if (shrinkOffset > 50.px && this.statusBarMode == 'light') {
- this.statusBarMode = 'dark';
- SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
- statusBarBrightness: Brightness.dark,
- statusBarIconBrightness: Brightness.dark,
- ));
- } else if (shrinkOffset <= 50.px && this.statusBarMode == 'dark') {
- this.statusBarMode = 'light';
- SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
- statusBarBrightness: Brightness.light,
- statusBarIconBrightness: Brightness.light,
- ));
- }
- }
- Color makeStickyHeaderBgColor(shrinkOffset) {
- final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255)
- .clamp(0, 255)
- .toInt();
- return Color.fromARGB(alpha, tColor.red, tColor.green, tColor.blue);
- }
- Color makeStickyHeaderTextColor(shrinkOffset, isIcon) {
- if (shrinkOffset <= 50.px) {
- return isIcon ? DColors.Main : Colors.transparent;
- } else {
- final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255)
- .clamp(0, 255).toInt();
- return Color.fromARGB(alpha, 255, 255, 255);
- }
- }
- Widget _buildSlider(BuildContext context, int index) {
- return CachedNetworkImage(
- fit: BoxFit.cover,
- imageUrl: data.storeInfo.slider_image[index],
- placeholder: (BuildContext context, String str) {
- return Center(child: Utils.loadingWidget);
- },
- );
- }
- @override
- Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
- this.updateStatusBarBrightness(shrinkOffset);
- var images = data.storeInfo.slider_image;
- return Container(
- height: this.maxExtent,
- width: MediaQuery
- .of(context)
- .size
- .width,
- child: Stack(
- fit: StackFit.expand,
- children: <Widget>[
- Container(
- height: 400.px,
- child: Swiper(
- autoplay: true,
- itemCount: images.length,
- itemBuilder: _buildSlider,
- pagination: SwiperPagination(),
- autoplayDisableOnInteraction: true,
- onTap: (index) {
- Utils.showPhoto(index: index, images: images);
- }
- )
- ),
- Positioned(left: 0, right: 0, top: 0,
- child: Container(
- color: this.makeStickyHeaderBgColor(shrinkOffset),
- child: SafeArea(
- bottom: false,
- child: Container(
- height: this.collapsedHeight,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- Container(
- margin: EdgeInsets.only(left: 10.px),
- child: InkWell(
- onTap: () => Navigator.pop(context),
- child: CircleAvatar(
- radius: 15.px,
- backgroundColor: tColor,
- child: Icon(
- Icons.chevron_left, color: Colors.white,
- size: 30.px),
- ),
- )
- ),
- Expanded(child: _buildHeaderMenu(shrinkOffset)),
- AnimatedSwitcher(
- transitionBuilder: (child, anim) =>
- ScaleTransition(child: child, scale: anim),
- duration: Duration(milliseconds: 300),
- child: Container(
- key: ValueKey(likeIcon),
- padding: EdgeInsets.only(left: 16.px, right: 10.px),
- child: InkWell(
- onTap: likeClick,
- child: CircleAvatar(
- radius: 15.px,
- backgroundColor: tColor,
- child: Icon(
- likeIcon, color: Colors.white, size: 21.px),
- ),
- )
- ),
- ),
- Container(
- padding: EdgeInsets.only(left: 16.px, right: 10.px),
- child: InkWell(
- onTap: _showShare,
- child: CircleAvatar(
- radius: 15,
- backgroundColor: tColor,
- child: Icon(Icons.share, color: Colors.white,
- size: 21.px),
- ),
- )
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- ],
- ),
- );
- }
- void _showShare() {
- showModalBottomSheet(
- context: Cache.context,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(
- topLeft: Radius.circular(10.px), topRight: Radius.circular(10.px)
- )),
- builder: (BuildContext context) {
- return ShareBar(data);
- });
- }
- Widget _buildHeaderMenu(double shrinkOffset) {
- var textColor = this.makeStickyHeaderTextColor(shrinkOffset, false);
- return Container(
- margin: EdgeInsets.only(left: 20.px, right: 20.px),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- InkWell(
- onTap: () { this.tabIdx = 0; },
- child: Container(
- width: 60.px,
- padding: EdgeInsets.only(left: 6.px, right: 6.px),
- child: Text('商品', style: TextStyle(color: textColor)
- ,textAlign: TextAlign.center),
- ),
- ),
- InkWell(
- onTap: () { this.tabIdx = 2; },
- child: Container(
- width: 60.px,
- padding: EdgeInsets.only(left: 6.px, right: 6.px),
- child: Text('评价', style: TextStyle(color: textColor)
- ,textAlign: TextAlign.center),
- ),
- ),
- InkWell(
- onTap: () { this.tabIdx = 1; },
- child: Container(
- width: 60.px,
- padding: EdgeInsets.only(left: 6.px, right: 6.px),
- child: Text('详情', style: TextStyle(color: textColor)
- ,textAlign: TextAlign.center),
- ),
- ),
- ],
- ),
- );
- }
- }
- class ProductContent extends StatelessWidget {
- final Details _details;
- ProductContent(this._details, {Key key}): super(key: key);
- _doSelectAttr (BuildContext context) {
- showModalBottomSheet(
- context: context,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.only(
- topLeft: Radius.circular(10.px), topRight: Radius.circular(10.px)
- )),
- builder: (BuildContext context) {
- return ProductAttrPage(_details, showAll: true);
- }
- );
- }
- Widget _buildProductAttr(BuildContext context) {
- var attr = this._details?.productAttr;
- if(attr != null && attr.length > 0) {
- var attrs = "";
- for(var item in attr) {
- attrs = "$attrs ${item.attr_name}";
- }
- return Container(
- color: Colors.white,
- margin: EdgeInsets.only(top: 6.px),
- padding: EdgeInsets.only(left: 16.px, right: 10.px, top: 8.px, bottom: 8.px),
- child: InkWell(
- onTap: () => _doSelectAttr(context),
- child: Flex(
- direction: Axis.horizontal,
- children: <Widget>[
- RichText(
- text: TextSpan(
- text: "选择:",
- style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w300, fontSize: 12.px),
- children: [
- TextSpan(
- text: "请选择$attrs",// attr[0].attr_value[0].attr,
- style: TextStyle(color: Colors.black, fontSize: 12.px),
- ),
- ]
- ),
- ),
- Spacer(),
- Icon(Icons.keyboard_arrow_right, color: Colors.grey)
- ],
- )
- ),
- );
- }
- return Container();
- }
- Widget _buildComment (BuildContext context) {
- return Container(
- color: Colors.white,
- margin: EdgeInsets.only(top: 6.px, bottom: 10.px),
- padding: EdgeInsets.only(left: 16.px, top: 10.px, right: 10.px, bottom: 10.px),
- child: InkWell(
- onTap: () {
- Navigator.pushNamed(context, RouteNames.comment, arguments: _details.uid.toString());
- },
- child: Row(
- children: [
- Text('用户评价(${_details.replyCount})'),
- Spacer(),
- Text('${_details.replyChance}% ', style: TextStyle(color: Colors.red)),
- Text('好评率'),
- Icon(Icons.keyboard_arrow_right, color: Colors.grey)
- ],
- ),
- ),
- );
- }
- @override
- Widget build(BuildContext context) {
- return Container(
- color: DColors.back,
- child: Column(
- children: <Widget>[
- Container(
- color: Colors.white,
- padding: EdgeInsets.only(left: 16.px, top: 16.px),
- child: Flex(
- direction: Axis.horizontal,
- children: <Widget>[
- RichText(
- text: TextSpan(
- text: I18n.$,
- style: TextStyle(fontSize: 12.px, color: Colors.red, fontWeight: FontWeight.bold),
- children: [
- TextSpan(
- text: Utils.formatRMB(Cache.isVip ? _details.storeInfo.vip_price : _details.storeInfo.price),
- style: TextStyle(fontSize: 20.px, color: DColors.price, fontWeight: FontWeight.bold)
- )
- ]
- ),
- ),
- Spacer()
- ],
- )
- ),
- Container(
- color: Colors.white,
- width: double.infinity,
- padding: EdgeInsets.only(left: 16.px, top: 10.px, right: 16.px),
- child: Text(_details.storeInfo.store_name, style: TextStyle(height: 1.3.px, fontWeight: FontWeight.bold))
- ),
- Container(
- color: Colors.white,
- padding: EdgeInsets.only(left: 16.px, top: 10.px, right: 16.px, bottom: 10.px),
- child: Flex(
- direction: Axis.horizontal,
- children: <Widget>[
- Text('原价:${Utils.formatRMB(_details?.storeInfo?.ot_price, show: true)}', style:
- TextStyle(color: Colors.grey, fontWeight: FontWeight.w300, fontSize: 12)),
- Spacer(),
- Text('库存:${_details?.storeInfo?.stock}', style:
- TextStyle(color: Colors.grey, fontWeight: FontWeight.w300, fontSize: 12)),
- Spacer(),
- Text('销量:${_details?.storeInfo?.fsales}', style:
- TextStyle(color: Colors.grey, fontWeight: FontWeight.w300, fontSize: 12))
- ],
- ),
- ),
- _buildProductAttr(context),
- _buildComment(context),
- Container(
- color: Colors.white,
- padding: EdgeInsets.only(left: 12.px, top: 10.px, right: 12.px),
- child: _details?.storeInfo?.description == null ? Container() : Html(data: _details.storeInfo.description),
- ),
- ],
- ),
- );
- }
- }
|