in this sample code which that have ListView and Container i would like to make simple gradient top of ListView when user is scrolling ListView items to right or left,
scrolling items to right should show gradient divider and scrolling to left should hide the divider, all of this visiblity should be have fade effect, could you help me how can i show and hide this gradient divider?
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(SampleShadow());
class SampleShadow extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'sample',
home: ShadowContainer(),
);
}
}
class ShadowContainer extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ShadowContainer();
}
class _ShadowContainer extends State<ShadowContainer> with TickerProviderStateMixin {
final ValueNotifier<bool> showShadow = ValueNotifier<bool>(false);
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
backgroundColor: Colors.indigo.withOpacity(0.7),
appBar: AppBar(),
body: Container(
margin: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 100.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
color: Colors.indigo[400],
borderRadius: BorderRadius.circular(100.0),
),
),
Expanded(
child: Stack(children: <Widget>[
NotificationListener<ScrollNotification>(
onNotification: (scrollState) {
if (scrollState is ScrollEndNotification && scrollState.metrics.pixels >= 100) {
showShadow.value = true;
print('show');
} else {
showShadow.value = false;
print('hide');
}
return false;
},
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(10.0),
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
color: Colors.purple[400],
borderRadius: BorderRadius.circular(100.0),
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
itemCount: 50),
),
ValueListenableBuilder<bool>(
valueListenable: showShadow,
builder: (context, value, child) => value? Container(
width: 10.0,
color: Colors.green,
): Container()),
]),
),
],
),
),
],
),
),
),
);
}
}
this solution work fine, thanks to #CopsOnRoad
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(SampleShadow());
class SampleShadow extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'sample',
home: ShadowContainer(),
);
}
}
class ShadowContainer extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ShadowContainer();
}
class _ShadowContainer extends State<ShadowContainer> with TickerProviderStateMixin {
final ValueNotifier<bool> showShadow = ValueNotifier<bool>(false);
AnimationController _animationController;
Animation<double> _fadeInFadeOut;
ScrollController _scrollController;
#override
void initState() {
super.initState();
_scrollController = ScrollController();
_animationController = AnimationController(vsync: this, duration: kThemeAnimationDuration);
_fadeInFadeOut = Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);
_animationController.forward();
_scrollController.addListener(() {
if (_scrollController.offset >= 10) {
showShadow.value = true;
} else {
showShadow.value = false;
}
});
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
backgroundColor: Colors.indigo.withOpacity(0.7),
appBar: AppBar(),
body: Container(
margin: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 100.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
color: Colors.indigo[400],
borderRadius: BorderRadius.circular(100.0),
),
),
Expanded(
child: Stack(children: <Widget>[
ListView.separated(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(10.0),
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
color: Colors.purple[400],
borderRadius: BorderRadius.circular(100.0),
),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
itemCount: 50),
ValueListenableBuilder<bool>(
valueListenable: showShadow,
builder: (context, value, child) {
if (value) {
_animationController.forward();
} else {
_animationController.reverse();
}
return FadeTransition(
opacity: _fadeInFadeOut,
child: Container(
width: 10.0,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.centerLeft,
colors: [Colors.black.withOpacity(0.5), Colors.transparent])),
),
);
}),
]),
),
],
),
),
],
),
),
),
);
}
}
Related
I have 2 pages, on one of them I have placed the code which displays indexers, I want to swipe on indexes to pass between pages. I can't figure out how to set it up. I will be grateful for your help.
Container(
child: SmoothPageIndicator(
controller: _pageController,
count: _pages.length,
effect: JumpingDotEffect(
dotHeight: 16,
dotWidth: 16,
jumpScale: .7,
verticalOffset: 15,
),
onDotClicked: _onchanged
),
),
List of pages:
List<Widget> _pages = [
Form_scan(),
Form_ttn()
];
Image indecators
I will be grateful if someone helps me to go from the first page to the second (swipe right) with these indicators
Try out below code:-
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:sizer/sizer.dart';
import 'package:livekeeping/Common/Constant.dart';
import 'package:livekeeping/Common/IconNames.dart';
import 'package:livekeeping/Common/Reusable.dart';
import 'package:livekeeping/Controller/Authentication/LoginWithOtpScreen.dart';
import 'package:lottie/lottie.dart';
class Onboarding extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return OnboardingState();
}
}
class OnboardingState extends State<Onboarding> {
int _current = 0;
Widget widgetChange = firstWidget();
var i = 0;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: double.maxFinite,
width: double.maxFinite,
child: Stack(
children: <Widget>[
PageView.builder(
onPageChanged: (pageIndex) {
setState(() {
_current = pageIndex;
});
},
itemCount: 4,
itemBuilder: (context, index) {
if (index == 0) {
return firstWidget();
} else if (index == 1) {
return secondWidget();
} else if (index == 2) {
return thirdWidget();
} else {
return fourthWidget();
}
},
),
Positioned(
bottom: 120,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [1, 2, 3, 4].map((url) {
int index = [1, 2, 3, 4].indexOf(url);
return Container(
width: 8.0,
height: 8.0,
margin:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _current == index
? Color.fromRGBO(0, 0, 0, 0.9)
: Color.fromRGBO(0, 0, 0, 0.4),
),
);
}).toList(),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ButtonTheme(
height: 45,
minWidth: 150,
child: new Center(
child: RaisedButton(
color: AppTextStyle.mainThemeColor(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
CupertinoPageRoute(
builder: (context) => Login()),
(route) => false);
},
child: new Text(
"Get Started".toUpperCase(),
style: GoogleFonts.openSans(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w500,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50)),
)),
),
],
),
)
],
),
),
);
}
}
class firstWidget extends StatefulWidget {
#override
_firstWidgetState createState() => _firstWidgetState();
}
class _firstWidgetState extends State<firstWidget>{
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: double.maxFinite,
width: double.maxFinite,
child: Image.asset(
SplashBG, // You can put your Background image as well
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 45.0.h,
width: 75.0.w,
child: Image.asset(Icon1),
),
],
),
],
),
],
);
}
}
class secondWidget extends StatefulWidget {
#override
_secondWidgetState createState() => _secondWidgetState();
}
class _secondWidgetState extends State<secondWidget>{
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: double.maxFinite,
width: double.maxFinite,
child: Image.asset(
SplashBG, // You can put your Background image as well
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 45.0.h,
width: 75.0.w,
child: Image.asset(Icon2),
),
],
),
],
),
],
);
}
}
class thirdWidget extends StatefulWidget {
#override
_thirdWidgetState createState() => _thirdWidgetState();
}
class _thirdWidgetState extends State<thirdWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: double.maxFinite,
width: double.maxFinite,
child: Image.asset(
SplashBG, // You can put your Background image as well
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 45.0.h,
width: 75.0.w,
child: Image.asset(Icon3),
),
],
),
],
),
],
);
}
}
class fourthWidget extends StatefulWidget {
#override
_fourthWidgetState createState() => _fourthWidgetState();
}
class _fourthWidgetState extends State<fourthWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: double.maxFinite,
width: double.maxFinite,
child: Image.asset(
SplashBG, // You can put your Background image as well
fit: BoxFit.cover,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 45.0.h,
width: 75.0.w,
child: Image.asset(Iconn4),
),
],
),
],
),
],
);
}
}
use PageView, :
PageView(
controller: _pageController,
children: [
Form_scan(),
Form_ttn()
],
),
make a PageController :
final PageController _pageController = PageController();
onDotClicked: _onchanged
void _onchanged(){
_pageController.animateToPage(
indexHalaman,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn
);
}
indexHalaman = page index, if you have 2 widget on pageView children then first page index = 0, next index page = 1, if you use stateful widget, dont forget to use setState, or maybe you can use bloc pattern for this
I am trying to achieve something like this in flutter. I have a horizontal scrollable which has these rounded containers. I want the width of these containers to shrink if the elements in the scrollable is more than 3 and it should expand as per the image if the elements are less than 2. What i want is exactly like this image, i have been reading about flexible widget but when i wrap it inside a scrollable row it gives layout specific issues. Any workaround to achieve this?
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SingleChildScrollView(
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
child: Row(
children: [
...List.generate(
9,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
height: 100,
width: 100,
),
),
)
],
)),
SizedBox(
height: 20,
),
Row(
children: [
...List.generate(
2,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 180,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(20))),
))
],
),
SizedBox(
height: 20,
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 350,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(20))),
),
],
)
],
),
),
);
}
The above build method produces the following result.(The values here are hardcoded and is just for demonstration). The list values are going to be dynamic and the desired result should be like the one in the video. How do i proceed with this?
https://streamable.com/w142je
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(debugShowCheckedModeBanner: false, home: MyHomePage());
}
}
class MyHomePage extends StatefulWidget {
final String hintText = 'hing';
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
#override
Widget build(BuildContext context) {
var list1 = List.filled(2, '2');
var list2 = List.filled(4, '4');
var list3 = List.filled(1, '1');
return SafeArea(
child: Scaffold(
body: Column(
children: [
_getList(context, list1),
_getList(context, list2),
_getList(context, list3),
],
),
),
);
}
Widget _getList(BuildContext context, List<String> list) {
bool ownSize = list.length == 2;
if (ownSize) {
return SingleChildScrollView(
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
child: Row(
children: list.map((t) => rowItem(t, 250)).toList(),
),
);
} else {
return Row(
children: list
.map(
(t) => Expanded(child: rowItem(t)),
)
.toList(),
);
}
}
Widget rowItem(String text, [double? width]) {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
height: 100,
width: width,
alignment: Alignment.center,
child: Text(text),
);
}
}
I have a tab bar with a PageView inside each TabBarView. Each PageView has a ListView and when I scroll it, how can I scroll from the Scaffold's SingleChildScrollView rather than just scrolling the ListView inside the TabBarView?
Currently, the tab bar stays on the same position when I scroll the TabBarView, which looks terrible. How can I scroll individual TabBarView from SingleChildScrollView?
I tried applying primary:false and NeverScrollablePhysics() to the ListView, but didn't turn out the way I wanted it to.
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
int currIndex = 0;
TabController _tabController;
#override
void initState() {
super.initState();
_tabController =
TabController(vsync: this, length: 3, initialIndex: currIndex);
_tabController.addListener(() {
_handleTabSelection();
});
}
void _handleTabSelection() {
setState(() {
currIndex = _tabController.index == null ? 0 : _tabController.index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: DefaultTabController(
length: 3,
initialIndex: 0,
child: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Container(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Container(
child: Text(
"HOME",
style: TextStyle(fontSize: 25),
)),
),
_buildTabBar(context),
],
),
),
Expanded(
child: _buildTabBarView(
context,
),
)
],
),
),
),
),
),
);
}
TabBarView _buildTabBarView(BuildContext context) {
return TabBarView(
controller: _tabController,
children: List.generate(
3,
(index) => Container(
color: Colors.red,
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 60,
color: Colors.blue,
child: Center(child: Text("$index"))),
);
},
),
)));
}
Widget _buildTabBar(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: Container(
height: 45,
width: double.infinity,
decoration: buildTabBarStyle(),
child: TabBar(
controller: _tabController,
isScrollable: false,
tabs: List.generate(
3,
(index) => Center(
child: Text(
"$index",
style: TextStyle(color: Colors.black),
),
)),
),
),
);
}
BoxDecoration buildTabBarStyle() {
return BoxDecoration(
color: Color.fromARGB(255, 230, 248, 255),
border: Border.all(
width: 1,
color: Colors.black,
),
borderRadius: BorderRadius.all(Radius.circular(10)),
);
}
}
I want to build a Carousel Slider that shows Product Images of a List I've made earlier, with an onPressed function that Navigates to another Route that shows the details of the tapped product, My product list is of name List< Productz >.
When I call the Carousel Slider method in my Home_Page Route I need "itemBuilder: (context, index)", so I wrapped it in a ListView.builder and wrapped that into a Container with defined height and width to avoid the error above, but on running the app nothing shows in the place where the Carousel Slider is supposed to be.. Am I doing this right?
My Carousel Slider code:
class CustomCarouselHomePage extends StatefulWidget {
final List<String> Productzz;
final Function press;
final Productz productz;
CustomCarouselHomePage(
{required this.Productzz, required this.press, required this.productz});
#override
_CustomCarouselHomePageState createState() => _CustomCarouselHomePageState();
}
class _CustomCarouselHomePageState extends State<CustomCarouselHomePage> {
int activeIndex = 0;
late final Function press;
late final Productz productz;
setActiveDot(index) {
setState(() {
activeIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: CarouselSlider(
options: CarouselOptions(
autoPlayInterval: Duration(seconds: 4),
autoPlayCurve: Curves.fastLinearToSlowEaseIn,
autoPlayAnimationDuration: Duration(seconds: 2),
viewportFraction: 1.0,
onPageChanged: (index,reason) {
setActiveDot(index);
},
),
items: widget.Productzz.map((productzz) {
return Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () => press (),
child: Stack(
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: Hero(
tag: "${productz.id}",
child: Image.asset(productz.item_image),
),
),
Container(
width: MediaQuery.of(context).size.width,
color: Colors.black.withOpacity(0.2),
),
],
), );
},
);
}).toList(),
),
),
Positioned(
left: 0,
right: 0,
bottom: 10,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: List.generate(widget.Productzz.length, (idx) {
return activeIndex == idx ? ActiveDot() : InactiveDot();
})),
)
],
);
}
}
class ActiveDot extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Container(
width: 25,
height: 8,
decoration: BoxDecoration(
color: white,
borderRadius: BorderRadius.circular(5),
),
),
);
}
}
class InactiveDot extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: grey,
borderRadius: BorderRadius.circular(5),
),
),
);
}
}
My code for when I call the Carousel Slider method in Home_Page Route:
Container(
height:MediaQuery.of(context).size.height,
child: ListView.builder(
itemBuilder: (context, index) => CustomCarouselHomePage(
Productzz: [],
productz: productz[index],
press: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(
productz: productz[index],
),
)),
),
),),
I'm trying to use Flow widget instead of BottomNavigationBar.
this is my code.
#override
Widget build(BuildContext context) {
final delegate = S.of(context);
return SafeArea(
child: Scaffold(
drawer: DrawerWidget(),
body: Stack(
children: [
_pages[_selectedPageIndex]['page'],
Positioned(
child: Container(
child: Flow(
delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
children: menuItems
.map<Widget>((IconData icon) => flowMenuItem(icon))
.toList(),
),
),
),
]),
}
But after adding left, right, bottom, or top properties to the Positioned widget, the Flow widget gon.
You can copy paste run full code below
You can use ConstrainedBox and set Stack fit and Positioned with Container
SafeArea(
child: Scaffold(
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment: Alignment.topLeft,
fit: StackFit.expand,
children: [
...
Positioned(
left: 0,
top: 0,
child: Container(
alignment: Alignment.topLeft,
width: MediaQuery.of(context).size.width,
height: 65,
child: FlowMenu()))
]),
),
),
);
working demo
full code
import 'package:flutter/cupertino.dart';
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: FlowTest(),
);
}
}
class FlowTest extends StatefulWidget {
#override
_FlowTestState createState() => _FlowTestState();
}
class _FlowTestState extends State<FlowTest> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment: Alignment.topLeft,
fit: StackFit.expand,
children: [
ListView(
shrinkWrap: true,
children: <Widget>[
Column(
children: <Widget>[
SizedBox(height: 20.0),
ListView.builder(
shrinkWrap: true,
itemCount: 5,
physics: PageScrollPhysics(),
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Container(
height: 50.0,
color: Colors.green,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.format_list_numbered,
color: Colors.white),
Padding(
padding: const EdgeInsets.only(right: 5.0)),
Text(index.toString(),
style: TextStyle(
fontSize: 20.0, color: Colors.white)),
],
),
),
Container(
child: GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
physics: PageScrollPhysics(),
childAspectRatio: 1.2,
children: List.generate(
8,
(index) {
return Container(
child: Card(
color: Colors.blue,
),
);
},
),
),
),
SizedBox(height: 20.0),
],
);
},
),
],
),
],
),
Positioned(
left: 0,
top: 0,
child: Container(
alignment: Alignment.topLeft,
width: MediaQuery.of(context).size.width,
height: 65,
child: FlowMenu()))
]),
),
),
);
}
}
class FlowMenu extends StatefulWidget {
#override
_FlowMenuState createState() => _FlowMenuState();
}
class _FlowMenuState extends State<FlowMenu>
with SingleTickerProviderStateMixin {
AnimationController menuAnimation;
IconData lastTapped = Icons.notifications;
final List<IconData> menuItems = <IconData>[
Icons.home,
Icons.new_releases,
Icons.notifications,
Icons.settings,
Icons.menu,
];
void _updateMenu(IconData icon) {
if (icon != Icons.menu) setState(() => lastTapped = icon);
}
#override
void initState() {
super.initState();
menuAnimation = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
}
Widget flowMenuItem(IconData icon) {
final double buttonDiameter =
MediaQuery.of(context).size.width / menuItems.length;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: RawMaterialButton(
fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
splashColor: Colors.amber[100],
shape: CircleBorder(),
constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
onPressed: () {
_updateMenu(icon);
menuAnimation.status == AnimationStatus.completed
? menuAnimation.reverse()
: menuAnimation.forward();
},
child: Icon(
icon,
color: Colors.white,
size: 45.0,
),
),
);
}
#override
Widget build(BuildContext context) {
return Container(
child: Flow(
delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
children: menuItems
.map<Widget>((IconData icon) => flowMenuItem(icon))
.toList(),
),
);
}
}
class FlowMenuDelegate extends FlowDelegate {
FlowMenuDelegate({this.menuAnimation}) : super(repaint: menuAnimation);
final Animation<double> menuAnimation;
#override
bool shouldRepaint(FlowMenuDelegate oldDelegate) {
return menuAnimation != oldDelegate.menuAnimation;
}
#override
void paintChildren(FlowPaintingContext context) {
double dx = 0.0;
for (int i = 0; i < context.childCount; ++i) {
dx = context.getChildSize(i).width * i;
context.paintChild(
i,
transform: Matrix4.translationValues(
dx * menuAnimation.value,
0,
0,
),
);
}
}
}