Flutter:更改滚动上的小部件不透明度和颜色的最佳方法

时间:2019-02-16 23:57:22

标签: scroll colors flutter opacity flutter-appbar

我的目标是在用户向下滚动时更改应用栏的颜色和不透明度。

我的逻辑是:

  • 滚动偏移量= 0:应用栏为红色,不透明度= 1
  • 0 <滚动偏移<40:应用栏为蓝色,不透明度= 0.4
  • 40 <=滚动偏移量:应用栏为蓝色,不透明度与滚动偏移量成比例

我想到了以下代码:

import 'package:flutter/material.dart';
import 'package:gradient_app_bar/gradient_app_bar.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _gradientColor1 = Colors.red[400];
  var _gradientColor2 = Colors.red[800];
  ScrollController _scrollViewController;

  void changeColor(){
    if((_scrollViewController.offset == 0) && (_gradientColor1 != Colors.red[400])){
      setState(() {
        _gradientColor1 = Colors.red[400];
        _gradientColor2 = Colors.red[800];
      });
    }else if((_scrollViewController.offset <= 40) && (_gradientColor1 != Color.fromRGBO(66,165,245 ,0.4))){
      setState(() {
        _gradientColor1 = Color.fromRGBO(66,165,245 ,0.4);
        _gradientColor2 = Color.fromRGBO(21,101,192 ,0.4);
      });
    }else if((_scrollViewController.offset <= 100) && (_scrollViewController.offset > 40)){
      var opacity = _scrollViewController.offset/100;
      setState(() {
        _gradientColor1 = Color.fromRGBO(66,165,245 ,opacity);
        _gradientColor2 = Color.fromRGBO(21,101,192 ,opacity);
      });
    }
  }

  @override
  void initState() {
    _scrollViewController = ScrollController(initialScrollOffset: 0.0);
    _scrollViewController.addListener(changeColor);
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: GradientAppBar(
        backgroundColorStart: _gradientColor1,
        backgroundColorEnd: _gradientColor2,
        elevation: 0,
      ),
      body: SingleChildScrollView(
        controller: _scrollViewController,
        child: Column(
          children: <Widget>[
            Container(color: Colors.red, height: 400,),
            Container(color: Colors.purple, height: 400,),
          ],
        ),
      ),
    );
  }
}

它可以按预期工作,但是在使用更复杂的UI时会变得迟钝。 在我的示例中,我使用的是GradientAppbar:https://github.com/joostlek/GradientAppBar

2 个答案:

答案 0 :(得分:0)

我认为最好的方法是使用AnimatedBuilder,并且您会看到主体中的第一个容器不会更改其颜色,因为窗口小部件状态没有更改 结果:

enter image description here

代码:

import 'dart:math';
import 'package:flutter/material.dart';

class ProductDetails extends StatefulWidget {
  @override
  _ProductDetailsState createState() => _ProductDetailsState();
}

class _ProductDetailsState extends State<ProductDetails>
    with TickerProviderStateMixin {
  AnimationController _ColorAnimationController;
  AnimationController _TextAnimationController;
  Animation _colorTween, _iconColorTween;
  Animation<Offset> _transTween;

  @override
  void initState() {
    _ColorAnimationController =
        AnimationController(vsync: this, duration: Duration(seconds: 0));
    _colorTween = ColorTween(begin: Colors.transparent, end: Color(0xFFee4c4f))
        .animate(_ColorAnimationController);
    _iconColorTween = ColorTween(begin: Colors.grey, end: Colors.white)
        .animate(_ColorAnimationController);


    _TextAnimationController =
        AnimationController(vsync: this, duration: Duration(seconds: 0));

    _transTween = Tween(begin: Offset(-10, 40), end: Offset(-10, 0))
        .animate(_TextAnimationController);

    super.initState();
  }

  bool _scrollListener(ScrollNotification scrollInfo) {
    if (scrollInfo.metrics.axis == Axis.vertical) {
      _ColorAnimationController.animateTo(scrollInfo.metrics.pixels / 350);

      _TextAnimationController.animateTo(
          (scrollInfo.metrics.pixels - 350) / 50);
      return true;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFEEEEEE),
      body: NotificationListener<ScrollNotification>(
        onNotification: _scrollListener,
        child: Container(
          height: double.infinity,
          child: Stack(
            children: <Widget>[
              SingleChildScrollView(
                child: Column(
                  children: <Widget>[
                    Container(
                      height: 150,
                      color:
                          Color((Random().nextDouble() * 0xFFFFFF).toInt() << 0)
                              .withOpacity(1),
                      width: 250,
                    ),
                    Container(
                      height: 150,
                      color: Colors.pink,
                      width: 250,
                    ),
                    Container(
                      height: 150,
                      color: Colors.deepOrange,
                      width: 250,
                    ),
                    Container(
                      height: 150,
                      color: Colors.red,
                      width: 250,
                    ),
                    Container(
                      height: 150,
                      color: Colors.white70,
                      width: 250,
                    ),
                  ],
                ),
              ),
              Container(
                height: 80,
                child: AnimatedBuilder(
                  animation: _ColorAnimationController,
                  builder: (context, child) => AppBar(
                    backgroundColor: _colorTween.value,
                    elevation: 0,
                    titleSpacing: 0.0,
                    title: Transform.translate(
                      offset: _transTween.value,
                      child: Text(
                        "اسم کالا اینجا",
                        style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                            fontSize: 16),
                      ),
                    ),
                    iconTheme: IconThemeData(
                      color: _iconColorTween.value,
                    ),
                    actions: <Widget>[
                      IconButton(
                        icon: Icon(
                          Icons.local_grocery_store,
                        ),
                        onPressed: () {
//                          Navigator.of(context).push(TutorialOverlay());
                        },
                      ),
                      IconButton(
                        icon: Icon(
                          Icons.more_vert,
                        ),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

答案 1 :(得分:0)

在这个小例子中,我执行以下操作:我根据某些条件更改我的 opacityAnimatedOpacity,即从顶部到底部的偏移量是大于还是小于 100 像素屏幕的。提到的偏移量是我在 RenderBoxGlobalKey 的帮助下获得的。此验证和事件发生在传递给我的 scrollListener 的函数中。这意味着每次我滚动时它们都会被触发。这是完整的代码。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _scrollController;
  GlobalKey widgetKey = GlobalKey();
  Offset widgetOffset;
  double _currentPosition;
  double opacity = 1;

  @override
  void initState() {
    _scrollController = ScrollController();
    _scrollController.addListener(_scrollListener);
    super.initState();
  }

  _scrollListener() {
    print('scrolling');

    RenderBox textFieldRenderBox = widgetKey.currentContext.findRenderObject();
    widgetOffset = textFieldRenderBox.localToGlobal(Offset.zero);
    _currentPosition = widgetOffset.dy;

    print(
        "widget position: $_currentPosition against: 100");

    if (100 > _currentPosition && _currentPosition > 1) {
      setState(() {
        opacity = _currentPosition / 100;
      });
    } else if (_currentPosition > 100 && opacity != 1) {
      opacity = 1;
    }
    else if (_currentPosition < 0 && opacity != 0) {
      opacity = 0;
    }
    print("opacity is: $opacity");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
          controller: _scrollController,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.red,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.red,
              ),
              AnimatedOpacity(
                key: widgetKey,
                duration: Duration(milliseconds: 1),
                opacity: opacity,
                child: Center(
                  child: Container(
                    margin: EdgeInsets.only(bottom: 50),
                    height: 100,
                    width: 100,
                    color: Colors.purpleAccent,
                  ),
                ),
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.red,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.red,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.red,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.teal,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.teal,
              ),
              Container(
                margin: EdgeInsets.only(bottom: 50),
                height: 100,
                width: 100,
                color: Colors.teal,
              ),
            ],
          )),
    );
  }
}

Scroll and animation behavior