flutter非常丝滑的组件(pageView的页面状态保持,完美的appBar,点击按钮复制到剪切版,照片保存到相册,tabBar切换)

前言

今天是痛苦与快乐并存的一天,痛苦是因为今天在想一些东西的时候,头都想炸了,快乐的是因为学了非常多的东西,解决了很多的问题,今天我就把自己在工作中用到的一些东西分享出来,供大家学习

    • pageView的页面状态保持
    • TabBar顶部隐藏,下滑渐现
    • 点击按钮,复制文字
    • 将本地照片或者是将网络照片保存到手机相册中
    • TabBar的切换加滑动切换效果
    • 一、PageView的页面状态保持?

                PageView(
                        scrollDirection: Axis.horizontal,
                        pageSnapping: true,
                        onPageChanged: (index) {
                          print(index);
                        },
                        children: [
                           Container(color:Colors.red),
                           PhoneWindowLayout(
                              child:Container(color:Color(0xff5BD8FF)),
                           )
                        ]
                  );
      
      //继承with AutomaticKeepAliveClientMixin轻松搞定
      class PhoneWindowLayout extends StatefulWidget {
        Widget widget;
      
        @override
        _PhoneWindowLayoutState createState() => _PhoneWindowLayoutState();
      }
      
      class _PhoneWindowLayoutState extends State<PhoneWindowLayout>
          with AutomaticKeepAliveClientMixin {
        @override
        Widget build(BuildContext context) {
          return widget
        }
      
        //状态保持
        @override
        bool get wantKeepAlive => true;
      }
      

      二、appBar顶部隐藏,下滑渐现

      在这里插入图片描述

       import 'package:flutter/material.dart';
      import 'package:flutter_screenutil/flutter_screenutil.dart';
      import 'package:home_insurance/my_ui/my_color.dart';
      
      //隐藏在顶端并且时透明的导航栏,上滑的时候才会出现
      class StackNavBar extends StatefulWidget {
      
       final IconData icon;
       final String title;
       final Widget child;
       Color iconColor = MyColor.inputBcColor;
      
        StackNavBar({this.iconColor,this.icon = Icons.keyboard_arrow_left,this.title = '',this.child});
      
        @override
        _StackStackNavBarState createState() => _StackStackNavBarState();
      }
      
      class _StackStackNavBarState extends State<StackNavBar>
          with TickerProviderStateMixin {
        AnimationController _colorAnimationController;
        AnimationController _textAnimationController;
        Animation _colorTween, _iconColorTween,_textColorTween;
      
        @override
        void initState() {
          _colorAnimationController =
              AnimationController(vsync: this, duration: Duration(seconds: 0));
          //一开始的颜色和往上滑动的颜色
          _colorTween = ColorTween(begin: Colors.transparent, end: MyColor.inputBcColor).animate(_colorAnimationController);
          _textColorTween = ColorTween(begin:Colors.transparent, end: Colors.black).animate(_colorAnimationController);
          _iconColorTween = ColorTween(begin:widget.iconColor, end: Colors.black).animate(_colorAnimationController);
          _textAnimationController =AnimationController(vsync: this, duration: Duration(seconds: 0));
          super.initState();
        }
      
        bool _scrollListener(ScrollNotification scrollInfo) {
          if (scrollInfo.metrics.axis == Axis.vertical) {
            //灵敏度
            _colorAnimationController.animateTo(scrollInfo.metrics.pixels / 10);
      
            _textAnimationController
                .animateTo((scrollInfo.metrics.pixels - 350) / 50);
            return true;
          }
          return false;
        }
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: NotificationListener<ScrollNotification>(
              onNotification: _scrollListener,
              child: Container(
                child: Stack(
                  children: <Widget>[
                    Container(
                      child:  widget.child,
                    ),
                    Container(
                      height:ScreenUtil().setHeight(100),
                      child:
                      AnimatedBuilder(
                        animation: _colorAnimationController,
                        builder: (context, child) => AppBar(
                          backgroundColor: _colorTween.value,
                          elevation: 0,
                          centerTitle: true,
                          title: Text(
                            widget.title,
                            style: TextStyle(
                                fontSize: 17,
                                fontWeight: FontWeight.w600,
                                color: _textColorTween.value
                            ),
                          ),
                          iconTheme: IconThemeData(
                            color: _iconColorTween.value,
                          ),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          );
        }
      }
      

      三.点击按钮,复制文字

      导入库

        clipboard: any
      
       InkWell(
                  child: Container(
                    decoration: BoxDecoration(
                        border: Border.all(width: 1, color: Color(0xff2DDD99)),
                        borderRadius: BorderRadius.circular(5)),
                    padding: EdgeInsets.symmetric(vertical: 2, horizontal: 3),
                    child: Text(
                      "复制",
                      style: TextStyle(color: Color(0xff2DDD99), fontSize: 13),
                    ),
                  ),
                  onTap: () {
                    FlutterClipboard.copy(order.number).then((value) {
                      Fluttertoast.showToast(
                          msg: "成功复制到剪切板", gravity: ToastGravity.BOTTOM);
                    });
                  },
                ),
      

      四.照片保存到相册

      导入库

         #保存照片到手机相册中
        image_gallery_saver: any
        cached_network_image: any
        permission_handler: any
      

      IOS配置文件

      在这里插入图片描述

       <key>NSPhotoLibraryUsageDescription</key>
       	<string></string>
          <key>NSPhotoLibraryAddUsageDescription</key>
          <string>请允许APP保存图片到相册</string>
      

      Android配置(含多项配置)

      在这里插入图片描述

      <!-- 开启网络访问权限 -->
          <uses-permission android:name="android.permission.INTERNET"/>
          <!--开启振动提示-->
          <uses-permission android:name="android.permission.VIBRATE"/>
          <!-- 开机启动权限 -->
          <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
          <!-- 摄像头权限 -->
          <uses-permission android:name="android.permission.CAMERA"/>
          <!-- 允许程序录制声音通过手机或耳机的麦克-->
          <uses-permission android:name="android.permission.RECORD_AUDIO"/>
          <!-- 在SDCard中创建与删除文件的权限 -->
          <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
          <!-- 往SDCard写入数据的权限 -->
          <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
          <!-- 在SDCard读取数据的权限 -->
          <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
      

      五.TabBar的切换加滑动切换效果

       class _CompleteOrderPageState extends State<CompleteOrderPage>
          with SingleTickerProviderStateMixin {
        final List<Tab> _myTabs = <Tab>[
          Tab(text: '待付款'),
          Tab(text: '已付款'),
          Tab(text: '已取消')
        ];
      
        @override
        Widget build(BuildContext context) {
          return
            DefaultTabController(
              initialIndex: 0,
              length: _myTabs.length,
              child: Scaffold(
                appBar: AppBar(
                  backgroundColor: Color(0xffFFFFFF),
                  elevation: 0,
                  centerTitle: true,
                  title: Text(
                    "全部保单",
                    style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
                  ),
                  bottom: PreferredSize(
                      preferredSize: Size(50, 50),
                      child: Theme(
                        child: TabBar(
                            unselectedLabelColor: Color(0xffBBBEC2),
                            labelPadding: EdgeInsets.symmetric(horizontal: 10),
                            indicator: MyUnderlineTabIndicator(
                                borderSide: BorderSide(width: 4.0, color: Color(0xff2DDD99))),
                            indicatorWeight: ScreenUtil().setWidth(3),
                            indicatorSize: TabBarIndicatorSize.label,
                            labelColor: Color(0xff242629),
                            tabs: _myTabs),
                        data: ThemeData(
                          splashColor: Colors.transparent,
                          highlightColor: Colors.transparent,
                        ),
                      )),
                ),
                body: new TabBarView(children: [
                  Scaffold(
                   
                  ),
                  Scaffold(
                   
                  ),
                  Scaffold(
                  
                  )
                ]),
              ),
          );
        }
      
      

      切换指示图标变得圆滑

        import 'package:flutter/material.dart';
      import 'package:flutter/widgets.dart';
       
      /// Used with [TabBar.indicator] to draw a horizontal line below the
      /// selected tab.
      ///
      /// The selected tab underline is inset from the tab's boundary by [insets].
      /// The [borderSide] defines the line's color and weight.
      ///
      /// The [TabBar.indicatorSize] property can be used to define the indicator's
      /// bounds in terms of its (centered) widget with [TabIndicatorSize.label],
      /// or the entire tab with [TabIndicatorSize.tab].
      class MyUnderlineTabIndicator extends Decoration {
        /// Create an underline style selected tab indicator.
        ///
        /// The [borderSide] and [insets] arguments must not be null.
        const MyUnderlineTabIndicator({
          this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
          this.insets = EdgeInsets.zero,
        })  : assert(borderSide != null),
              assert(insets != null);
       
        /// The color and weight of the horizontal line drawn below the selected tab.
        final BorderSide borderSide;
       
        /// Locates the selected tab's underline relative to the tab's boundary.
        ///
        /// The [TabBar.indicatorSize] property can be used to define the
        /// tab indicator's bounds in terms of its (centered) tab widget with
        /// [TabIndicatorSize.label], or the entire tab with [TabIndicatorSize.tab].
        final EdgeInsetsGeometry insets;
       
        @override
        Decoration lerpFrom(Decoration a, double t) {
          if (a is UnderlineTabIndicator) {
            return UnderlineTabIndicator(
              borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
              insets: EdgeInsetsGeometry.lerp(a.insets, insets, t),
            );
          }
          return super.lerpFrom(a, t);
        }
       
        @override
        Decoration lerpTo(Decoration b, double t) {
          if (b is MyUnderlineTabIndicator) {
            return MyUnderlineTabIndicator(
              borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
              insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
            );
          }
          return super.lerpTo(b, t);
        }
       
        @override
        _MyUnderlinePainter createBoxPainter([VoidCallback onChanged]) {
          return _MyUnderlinePainter(this, onChanged);
        }
      }
       
      class _MyUnderlinePainter extends BoxPainter {
        _MyUnderlinePainter(this.decoration, VoidCallback onChanged)
            : assert(decoration != null),
              super(onChanged);
       
        final MyUnderlineTabIndicator decoration;
       
        BorderSide get borderSide => decoration.borderSide;
        EdgeInsetsGeometry get insets => decoration.insets;
       
        Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
          assert(rect != null);
          assert(textDirection != null);
          final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
       
          //希望的宽度
          double wantWidth = 42;
          //取中间坐标
          double cw = (indicator.left + indicator.right) / 2;
          return Rect.fromLTWH(cw - wantWidth / 2,
              indicator.bottom - borderSide.width, wantWidth, borderSide.width);
        }
       
        @override
        void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
          assert(configuration != null);
          assert(configuration.size != null);
          final Rect rect = offset & configuration.size;
          final TextDirection textDirection = configuration.textDirection;
          final Rect indicator =
              _indicatorRectFor(rect, textDirection).deflate(borderSide.width / 2.0);
          // 改为圆角
          final Paint paint = borderSide.toPaint()..strokeCap = StrokeCap.round;
          canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
        }
      }
      
    • 0
      点赞
    • 2
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值