My goal is to change the color and the opacity of the appbar when user scrolls down.
My logic is:
I came up with the following code:
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,),
],
),
),
);
}
}
It works as expected but it becomes laggy with a more complicated UI. In my example I'm using GradientAppbar: https://github.com/joostlek/GradientAppBar
Surround the widget which you want to change the opacity and the container with Stack widget. Then wrap the widget which you want to change the opacity with Opacity widget and mention required opacity. Show activity on this post. Simply make the main container background decoration an opaque color.
i think the best approach Will be using AnimatedBuilder
and you will see that first container in body will not changed its color because widget state have not changed
and the result :
code:
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: () {},
),
],
),
),
),
],
),
),
),
);
}
}
In this little example I do the following: I change the opacity
of my AnimatedOpacity
depending on some conditions, namely if its offset is either greater or less than 100 pixels from the top to the bottom of the screen. I obtain the mentioned offset with the help of RenderBox
and GlobalKey
. This validations and events happen within the function passed to my scrollListener
. That means that they get triggered every time I scroll. Here is the full code.
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,
),
],
)),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With