美文网首页
Flutter沉浸式Banner + 滚动修改状态栏颜色

Flutter沉浸式Banner + 滚动修改状态栏颜色

作者: 醉挽清风_666 | 来源:发表于2019-04-12 20:12 被阅读0次

    Flutter沉浸式Banner + 滚动修改状态栏颜色

    PS:最近项目有个非人类需求,顶部放轮播广告,滚动切换状态栏颜色。对于Flutter小白来说,首先想到的是度娘,然并软,搜了一圈没找到类似的,轮播图资源还蛮多的,但是滚动改状态栏却找不到。。。瞬间懵逼,还是老老实实手敲轮子吧~(怕忘记了记录下)

    • 国际惯例先上图


      banner.gif

    废话不多说,直接上代码:

    • 一个依赖
     flutter_statusbarcolor: any
    
    • 一个轮播图的实体类CarouselSlider :
    library carousel_slider;
    
    import 'dart:async';
    
    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    
    class CarouselSlider extends StatefulWidget {
      CarouselSlider({
        @required this.items,
        this.height,
        this.aspectRatio: 16 / 9,
        this.viewportFraction: 0.8,
        this.initialPage: 0,
        int realPage: 10000,
        this.enableInfiniteScroll: true,
        this.reverse: false,
        this.autoPlay: false,
        Duration autoPlayInterval,
        Duration autoPlayAnimationDuration,
        this.autoPlayCurve: Curves.fastOutSlowIn,
        this.pauseAutoPlayOnTouch,
        bool enlargeCenterPage,
        Function(int index) onPageChanged,
        this.scrollDirection: Axis.horizontal,
        @Deprecated('Use "autoPlayInterval" instead') this.interval,
        @Deprecated('Use "autoPlayAnimationDuration" instead') this.autoPlayDuration,
        @Deprecated('Use "enlargeCenterPage" instead') this.distortion,
        @Deprecated('Use "onPageChanged" instead') this.updateCallback,
      })  : assert(autoPlayInterval == null || interval == null,
      'use autoPlayInterval (preferable) or interval (deprecated), but not both.'),
            assert(autoPlayAnimationDuration == null || autoPlayDuration == null,
            'use autoPlayAnimationDuration (preferable) or autoPlayDuration (deprecated), but not both.'),
            assert(enlargeCenterPage == null || distortion == null,
            'use enlargeCenterPage (preferable) or distortion (deprecated), but not both.'),
            assert(onPageChanged == null || updateCallback == null,
            'use onPageChanged (preferable) or updateCallback (deprecated), but not both.'),
            this.autoPlayInterval = (autoPlayInterval ?? interval) ?? const Duration(seconds: 4),
            this.enlargeCenterPage = (enlargeCenterPage ?? distortion) ?? false,
            this.autoPlayAnimationDuration =
                (autoPlayAnimationDuration ?? autoPlayDuration) ?? const Duration(milliseconds: 800),
            this.onPageChanged = onPageChanged ?? updateCallback,
            this.realPage = enableInfiniteScroll ? realPage + initialPage : initialPage,
            this.pageController = PageController(
              viewportFraction: viewportFraction,
              initialPage: enableInfiniteScroll ? realPage + initialPage : initialPage,
            );
    
      /// The widgets to be shown in the carousel.
      final List<Widget> items;
    
      /// Set carousel height and overrides any existing [aspectRatio].
      final double height;
    
      /// Aspect ratio is used if no height have been declared.
      ///
      /// Defaults to 16:9 aspect ratio.
      final double aspectRatio;
    
      /// The fraction of the viewport that each page should occupy.
      ///
      /// Defaults to 0.8, which means each page fills 80% of the carousel.
      final num viewportFraction;
    
      /// The initial page to show when first creating the [CarouselSlider].
      ///
      /// Defaults to 0.
      final num initialPage;
    
      /// The actual index of the [PageView].
      ///
      /// This value can be ignored unless you know the carousel will be scrolled
      /// backwards more then 10000 pages.
      /// Defaults to 10000 to simulate infinite backwards scrolling.
      final num realPage;
    
      ///Determines if carousel should loop infinitely or be limited to item length.
      ///
      ///Defaults to true, i.e. infinite loop.
      final bool enableInfiniteScroll;
    
      /// Reverse the order of items if set to true.
      ///
      /// Defaults to false.
      final bool reverse;
    
      /// Enables auto play, sliding one page at a time.
      ///
      /// Use [autoPlayInterval] to determent the frequency of slides.
      /// Defaults to false.
      final bool autoPlay;
    
      /// Sets Duration to determent the frequency of slides when
      ///
      /// [autoPlay] is set to true.
      /// Defaults to 4 seconds.
      final Duration autoPlayInterval;
    
      /// (Deprecated, use [autoPlayInterval] instead) Changed for ambiguous intent.
      /// interval did not explain what the variable was used for.
      /// Changing it to [autoPlayInterval] describes where it's used and for what.
      ///
      /// Sets Duration to determent the frequency of slides when
      /// [autoPlay] is set to true.
      /// Defaults to 4 seconds.
      @deprecated
      final Duration interval;
    
      /// (Deprecated, use [autoPlayAnimationDuration] instead) Changed for misleading intent.
      /// 'autoPlayDuration' implies reference to the duration of the entire auto play instance.
      /// Changed to 'autoPlayAnimationDuration' to sympathize its referring to the length of
      /// the animation between the page transitions.
      ///
      /// The animation duration between two transitioning pages while in auto playback.
      /// Defaults to 800 ms.
      @deprecated
      final Duration autoPlayDuration;
    
      /// The animation duration between two transitioning pages while in auto playback.
      ///
      /// Defaults to 800 ms.
      final Duration autoPlayAnimationDuration;
    
      /// Determines the animation curve physics.
      ///
      /// Defaults to [Curves.fastOutSlowIn].
      final Curve autoPlayCurve;
    
      /// Sets a timer on touch detected that pause the auto play with
      /// the given [Duration].
      ///
      /// Touch Detection is only active if [autoPlay] is true.
      final Duration pauseAutoPlayOnTouch;
    
      /// (Deprecated, use [enlargeCenterPage] instead) Changed for ambiguous intent.
      /// 'distortion' provided no information on how the image was distorted.
      /// [enlargeCenterPage] is self documenting, thus making it easier to understand
      /// the api.
      ///
      /// Determines if current page should be larger then the side images,
      /// creating a feeling of depth in the carousel.
      /// Defaults to false.
      @deprecated
      final bool distortion;
    
      /// Determines if current page should be larger then the side images,
      /// creating a feeling of depth in the carousel.
      ///
      /// Defaults to false.
      final bool enlargeCenterPage;
    
      /// The axis along which the page view scrolls.
      ///
      /// Defaults to [Axis.horizontal].
      final Axis scrollDirection;
    
      /// (Deprecated, use [onPageChanged] instead) Changed for ambiguous intent.
      /// 'updateCallback' provided no information on when the callback was called.
      /// Refactored to following the [PageView] naming convention.
      ///
      /// Called whenever the page in the center of the viewport changes.
      @deprecated
      final Function updateCallback;
    
      /// Called whenever the page in the center of the viewport changes.
      final Function(int index) onPageChanged;
    
      /// [pageController] is created using the properties passed to the constructor
      /// and can be used to control the [PageView] it is passed to.
      final PageController pageController;
    
      /// Animates the controlled [CarouselSlider] to the next page.
      ///
      /// The animation lasts for the given duration and follows the given curve.
      /// The returned [Future] resolves when the animation completes.
      Future<void> nextPage({Duration duration, Curve curve}) {
      return pageController.nextPage(duration: duration, curve: curve);
      }
    
      /// Animates the controlled [CarouselSlider] to the previous page.
      ///
      /// The animation lasts for the given duration and follows the given curve.
      /// The returned [Future] resolves when the animation completes.
      Future<void> previousPage({Duration duration, Curve curve}) {
      return pageController.previousPage(duration: duration, curve: curve);
      }
    
      /// Changes which page is displayed in the controlled [CarouselSlider].
      ///
      /// Jumps the page position from its current value to the given value,
      /// without animation, and without checking if the new value is in range.
      void jumpToPage(int page) {
      final index = _getRealIndex(pageController.page.toInt(), realPage, items.length);
      return pageController.jumpToPage(pageController.page.toInt() + page - index);
      }
    
      /// Animates the controlled [CarouselSlider] from the current page to the given page.
      ///
      /// The animation lasts for the given duration and follows the given curve.
      /// The returned [Future] resolves when the animation completes.
      Future<void> animateToPage(int page, {Duration duration, Curve curve}) {
      final index = _getRealIndex(pageController.page.toInt(), realPage, items.length);
      return pageController.animateToPage(pageController.page.toInt() + page - index,
      duration: duration, curve: curve);
      }
    
      @override
      _CarouselSliderState createState() => _CarouselSliderState();
    }
    
    class _CarouselSliderState extends State<CarouselSlider> with TickerProviderStateMixin {
      Timer timer;
    
      @override
      void initState() {
        super.initState();
        timer = getTimer();
      }
    
      Timer getTimer() {
        return Timer.periodic(widget.autoPlayInterval, (_) {
          if (widget.autoPlay) {
            widget.pageController
                .nextPage(duration: widget.autoPlayAnimationDuration, curve: widget.autoPlayCurve);
          }
        });
      }
    
      void pauseOnTouch() {
        timer.cancel();
        timer = Timer(widget.pauseAutoPlayOnTouch, () {
          timer = getTimer();
        });
      }
    
      Widget getWrapper(Widget child) {
        if (widget.height != null) {
          final Widget wrapper = Container(height: widget.height, child: child);
          return widget.autoPlay && widget.pauseAutoPlayOnTouch != null
              ? addGestureDetection(wrapper)
              : wrapper;
        } else {
          final Widget wrapper = AspectRatio(aspectRatio: widget.aspectRatio, child: child);
          return widget.autoPlay && widget.pauseAutoPlayOnTouch != null
              ? addGestureDetection(wrapper)
              : wrapper;
        }
      }
    
      Widget addGestureDetection(Widget child) =>
          GestureDetector(onPanDown: (_) => pauseOnTouch(), child: child);
    
      @override
      void dispose() {
        super.dispose();
        timer?.cancel();
      }
    
      @override
      Widget build(BuildContext context) {
        return getWrapper(PageView.builder(
          scrollDirection: widget.scrollDirection,
          controller: widget.pageController,
          reverse: widget.reverse,
          itemCount: widget.enableInfiniteScroll ? null : widget.items.length,
          onPageChanged: (int index) {
            int currentPage = _getRealIndex(index, widget.realPage, widget.items.length);
            if (widget.onPageChanged != null) {
              widget.onPageChanged(currentPage);
            }
          },
          itemBuilder: (BuildContext context, int i) {
            final int index =
            _getRealIndex(i + widget.initialPage, widget.realPage, widget.items.length);
    
            return AnimatedBuilder(
              animation: widget.pageController,
              child: widget.items[index],
              builder: (BuildContext context, child) {
                // on the first render, the pageController.page is null,
                // this is a dirty hack
                if (widget.pageController.position.minScrollExtent == null ||
                    widget.pageController.position.maxScrollExtent == null) {
                  Future.delayed(Duration(microseconds: 1), () {
                    setState(() {});
                  });
                  return Container();
                }
                double value = widget.pageController.page - i;
                value = (1 - (value.abs() * 0.3)).clamp(0.0, 1.0);
    
                final double height =
                    widget.height ?? MediaQuery.of(context).size.width * (1 / widget.aspectRatio);
                final double distortionValue =
                widget.enlargeCenterPage ? Curves.easeOut.transform(value) : 1.0;
    
                return Center(child: SizedBox(height: distortionValue * height, child: child));
              },
            );
          },
        ));
      }
    }
    
    /// Converts an index of a set size to the corresponding index of a collection of another size
    /// as if they were circular.
    ///
    /// Takes a [position] from collection Foo, a [base] from where Foo's index originated
    /// and the [length] of a second collection Baa, for which the correlating index is sought.
    ///
    /// For example; We have a Carousel of 10000(simulating infinity) but only 6 images.
    /// We need to repeat the images to give the illusion of a never ending stream.
    /// By calling _getRealIndex with position and base we get an offset.
    /// This offset modulo our length, 6, will return a number between 0 and 5, which represent the image
    /// to be placed in the given position.
    int _getRealIndex(int position, int base, int length) {
      final int offset = position - base;
      return _remainder(offset, length);
    }
    
    /// Returns the remainder of the modulo operation [input] % [source], and adjust it for
    /// negative values.
    int _remainder(int input, int source) {
      final int result = input % source;
      return result < 0 ? source + result : result;
    }
    
    • banner 加指示器
    /**
       * banner图
       */
      Widget buildBanner(BuildContext context) {
        return new Stack(
          children: <Widget>[
            getFullScreenCarousel(context),
            Positioned(top: 120,
              left: 0.0,
              right: 0.0,
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: map<Widget>(_images, (index, url) {
                  return Container(
                    width: 5.0,
                    height: 5.0,
                    margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
                    decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: _current == index
                            ? Color.fromRGBO(0, 0, 255, 0.9)
                            : Color.fromRGBO(255, 255, 255, 1)
                    ),
                  );
                }),
              ),)
          ],
        );
      }
    
      /**
       * 全屏轮播图
       */
      CarouselSlider getFullScreenCarousel(BuildContext mediaContext) {
        return CarouselSlider(
          autoPlay: true,
          viewportFraction: 1.0,
          aspectRatio: MediaQuery
              .of(mediaContext)
              .size
              .aspectRatio,
          height: 150,
          onPageChanged: (int index) {
            setState(() {
              _current = index;
            });
          },
          items: _images.map((url) {
            return new Builder(
              builder: (BuildContext context) {
                return new Container(
                    width: MediaQuery
                        .of(context)
                        .size
                        .width,
                    child: ClipRRect(
                        borderRadius: BorderRadius.circular(0.0),
                        child: new Image.asset(
                          url,
                          fit: BoxFit.fill,
                          height: 200,
                        )
                    )
                );
              },
            );
          }).toList(),
        );
      }
    
    List<T> map<T>(List list, Function handler) {
        List<T> result = [];
        for (var i = 0; i < list.length; i++) {
          result.add(handler(i, list[i]));
        }
        return result;
      }
    
    • 所有代码集合
    import 'package:cardservice_flutter2/utils/CarouselSlider.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
    
    class NewHomePage extends StatefulWidget {
    
      var parentContext;
    
      NewHomePage(this.parentContext);
    
      @override
      _NewHomePageState createState() => _NewHomePageState();
    }
    
    class _NewHomePageState extends State<NewHomePage> {
    
      List<String> _images =
      [
        'images/one_banner.png',
        'images/two_banner.png',
        'images/three_banner.png',
        'images/four_banner.png'
      ];
    
      int _current = 0;
    
    ScrollController _controller = new ScrollController();
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        _controller.addListener(() {
          if (_controller.offset > 150) {
            setState(() {
              _apla = 1;
    
            });
            FlutterStatusbarcolor.setStatusBarWhiteForeground(false);
          }
          else
            {
              setState(() {
                _isChange = true;
                _apla = _controller.offset / 150.0;
                print("透明度${_apla}");
    
              });
              FlutterStatusbarcolor.setStatusBarWhiteForeground(true);
            }
        }
        );
      }
    
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
            appBar: PreferredSize(
                child: Offstage(
                  child: AppBar(
                  ),
                  offstage: true,
                ),
                preferredSize: Size(MediaQuery
                    .of(context)
                    .size
                    .width, 0)),
            body: new Container(
              padding: EdgeInsets.all(0),
              child: new ListView(
                    controller: _controller,
                    children: <Widget>[
                      buildBanner(context),
                      ListView.builder(
                          shrinkWrap: true,
                          physics: new NeverScrollableScrollPhysics(),
                          itemCount: 50,
                          itemBuilder: (context, index) {
                            return new Text("66666");
                          })
                    ],
                  )
            )
        );
      }
    
      /**
       * banner图
       */
      Widget buildBanner(BuildContext context) {
        return new Stack(
          children: <Widget>[
            getFullScreenCarousel(context),
            Positioned(top: 120,
              left: 0.0,
              right: 0.0,
              child: new Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: map<Widget>(_images, (index, url) {
                  return Container(
                    width: 5.0,
                    height: 5.0,
                    margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
                    decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: _current == index
                            ? Color.fromRGBO(0, 0, 255, 0.9)
                            : Color.fromRGBO(255, 255, 255, 1)
                    ),
                  );
                }),
              ),)
          ],
        );
      }
    
      /**
       * 全屏轮播图
       */
      CarouselSlider getFullScreenCarousel(BuildContext mediaContext) {
        return CarouselSlider(
          autoPlay: true,
          viewportFraction: 1.0,
          aspectRatio: MediaQuery
              .of(mediaContext)
              .size
              .aspectRatio,
          height: 150,
          onPageChanged: (int index) {
            setState(() {
              _current = index;
            });
          },
          items: _images.map((url) {
            return new Builder(
              builder: (BuildContext context) {
                return new Container(
                    width: MediaQuery
                        .of(context)
                        .size
                        .width,
                    child: ClipRRect(
                        borderRadius: BorderRadius.circular(0.0),
                        child: new Image.asset(
                          url,
                          fit: BoxFit.fill,
                          height: 200,
                        )
                    )
                );
              },
            );
          }).toList(),
        );
      }
    
    
      List<T> map<T>(List list, Function handler) {
        List<T> result = [];
        for (var i = 0; i < list.length; i++) {
          result.add(handler(i, list[i]));
        }
        return result;
      }
    
    }
    
    记录一下,也希望本文能对读者有帮助~~

    相关文章

      网友评论

          本文标题:Flutter沉浸式Banner + 滚动修改状态栏颜色

          本文链接:https://www.haomeiwen.com/subject/soyhwqtx.html