Expandable Floating Action Button doesn't work - android

I'm trying to use the Expandable Floating Action Button available at the link: https://docs.flutter.dev/cookbook/effects/expandable-fab but the internal buttons don't work, I press them and nothing happens. I'm using this widget inside a Stack. Below the Expandable Floating Action Button code is where it is used;
#immutable
class ExpandableFab extends StatefulWidget {
final bool? initialOpen;
final double distance;
final List<Widget> children;
final VoidCallback onTap;
const ExpandableFab({
super.key,
this.initialOpen,
required this.distance,
required this.children,
required this.onTap,
});
#override
_ExpandableFabState createState() => _ExpandableFabState();
}
class _ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _expandAnimation;
bool _open = false;
#override
void initState() {
super.initState();
_open = widget.initialOpen ?? false;
_controller = AnimationController(
value: _open ? 1.0 : 0.0,
duration: const Duration(milliseconds: 250),
vsync: this,
);
_expandAnimation = CurvedAnimation(
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.easeOutQuad,
parent: _controller,
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
void _toggle() {
setState(() {
_open = !_open;
if (_open) {
_controller.forward();
} else {
_controller.reverse();
}
});
}
#override
Widget build(BuildContext context) {
return SizedBox.expand(
child: Stack(
alignment: Alignment.bottomRight,
clipBehavior: Clip.none,
children: [
_buildTapToCloseFab(),
..._buildExpandingActionButtons(),
_buildTapToOpenFab()
],
),
);
}
Widget _buildTapToCloseFab() {
return SizedBox(
width: 56.0,
height: 56.0,
child: Center(
child: Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
elevation: 4.0,
child: InkWell(
onTap: _toggle,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.close, color: Colors.deepPurple),
),
),
),
),
);
}
List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
final step = 90.0 / (count - 1);
for (var i = 0, angleInDegrees = 0.0;
i < count;
i++, angleInDegrees += step) {
children.add(
_ExpandingActionButton(
directionInDegrees: angleInDegrees,
maxDistance: widget.distance,
progress: _expandAnimation,
child: widget.children[i],
),
);
}
return children;
}
Widget _buildTapToOpenFab() {
return IgnorePointer(
ignoring: _open,
child: AnimatedContainer(
transformAlignment: Alignment.center,
transform: Matrix4.diagonal3Values(
_open ? 0.7 : 1.0,
_open ? 0.7 : 1.0,
1.0,
),
duration: const Duration(milliseconds: 250),
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
child: AnimatedOpacity(
opacity: _open ? 0.0 : 1.0,
curve: const Interval(0.25, 1.0, curve: Curves.easeInOut),
duration: const Duration(milliseconds: 250),
child: ClipOval(
child: Material(
color: Colors.deepPurple, // Button color
child: InkWell(
//splashColor: Colors.white, // Splash color
onLongPress: _toggle,
onTap: widget.onTap,
onDoubleTap: () {},
child: const SizedBox(
width: 56,
height: 56,
child: Icon(Icons.add, color: Colors.white),
),
),
),
),
),
),
);
}
}
#immutable
class _ExpandingActionButton extends StatelessWidget {
const _ExpandingActionButton({
Key? key,
required this.directionInDegrees,
required this.maxDistance,
required this.progress,
required this.child,
}) : super(key: key);
final double directionInDegrees;
final double maxDistance;
final Animation<double> progress;
final Widget child;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: progress,
builder: (context, child) {
final offset = Offset.fromDirection(
directionInDegrees * (math.pi / 180.0),
progress.value * maxDistance,
);
return Positioned(
right: 4.0 + offset.dx,
bottom: 4.0 + offset.dy,
child: Transform.rotate(
angle: (1.0 - progress.value) * math.pi / 2,
child: child!,
),
);
},
child: FadeTransition(opacity: progress, child: child),
);
}
}
#immutable
class ActionButton extends StatelessWidget {
const ActionButton({
super.key,
this.onPressed,
required this.icon,
});
final VoidCallback? onPressed;
final Widget icon;
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
color: Colors.deepPurple,
elevation: 4.0,
child: IconTheme.merge(
data: theme.primaryIconTheme,
child: IconButton(onPressed: onPressed, icon: icon),
),
);
}
}
Expandable FLoating Action Button is used within this Stack:
Stack(
children: <Widget>[
// some widget
Positioned(
right: 30.0,
bottom: 140.0,
child: SizedBox(
width: 60.0,
height: 60.0,
child: ExpandableFab(
distance: 112.0,
children: <Widget>[
ActionButton(
icon: const Icon(Icons.delete),
onPressed: () {
debugPrint("test1");
},
),
ActionButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
debugPrint("test2");
},
),
ActionButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
debugPrint("test3");
},
)
],
onTap: () {
debugPrint("test4");
},
),
),
),
],
),

If you do want to use it in the stack, place the code for the ExpandedButton in the last position so that it gets reported with touch events.
Code sample:
Stack(
children: <Widget>[
// All your other widgets go up here
RandomWidget(),
RandomWidget2(),
........... //other widgets
// This comes last.
Positioned(
right: 30.0,
bottom: 140.0,
child: SizedBox(
width: 60.0,
height: 60.0,
child: ExpandableFab(
distance: 112.0,
children: <Widget>[
ActionButton(
icon: const Icon(Icons.delete),
onPressed: () {
debugPrint("test1");
},
),
ActionButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
debugPrint("test2");
},
),
ActionButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
debugPrint("test3");
},
)
],
onTap: () {
debugPrint("test4");
},
),
),
),
],
),
By the way, Why are you using this in a stack? You can pretty much use it as a FloatingActionButton by passing it to floatingActionButton parameter of the Scaffold.

Related

Updating State of StatefulWidget from other StatefulWidget in Flutter?

i am trying to update the state of the animated switcher in the middle area. i am trying to do this using a setstate in the lower area. but it does not work.
the first thing i did is to create a variable with a boolean data type in the home class.
then i passed the variable to both the middle area and the lower area
the idea was that if the same variable is passed to the class whose state i am trying to update, and the class with the set state, it would work. but it seems i am wrong. i would appreciate some assistance.
the boolean variable i am trying to make work is the _rep variable
This is the Home widget
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late AnimationController _controller;
late Animation<Offset> _animation;
late Animation<Offset> _anime;
bool _rep = true;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2)
);
_animation = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,3.0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
_anime = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,-0.55),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
super.initState();
}
#override
void dispose() {
_animationController.dispose();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 3.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TopIcon(icons: Icons.arrow_back, color:Colors.grey.shade300 ,),
SizedBox(
height: 13.h,
width: 13.w,
child: Image.asset('assets/images/download.png')
),
TopIcon(icons: Icons.shopping_bag_outlined, color: Colors.grey.shade300,),
],
),
SizedBox(
height: 3.h,
),
Text('Frappuccino',
style: TextStyle(
fontSize: 27.sp,
fontWeight: FontWeight.bold
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('White Chocolate',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey.shade400
),
),
),
MiddleArea(
controller: _animationController,
animation: _animation,
rep: _rep,
),
LowerArea(controller: _animationController, anime: _anime, rep = _rep),
],
),
),
),
);
}
}
This is the middle area
class MiddleArea extends StatefulWidget {
MiddleArea({Key? key, required this.controller, required this.animation, required this.rep}) : super(key: key);
AnimationController controller;
Animation<Offset> animation;
final bool rep;
#override
State<MiddleArea> createState() => _MiddleAreaState();
}
class _MiddleAreaState extends State<MiddleArea> {
bool _flag = true;
bool _favourite = true;
#override
Widget build(BuildContext context) {
print(widget.rep);
return SizedBox(
height: 52.h,
child: Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 135.0),
child: Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade200,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade100,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade50,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
SizeAndFave(text: 'Preference'),
SizeAndFave(text: 'Fave!')
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: (){
setState(() {
_flag = !_flag;
});
},
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: widget.rep == true?Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
)
),
GestureDetector(
onTap: (){
setState(() {
_favourite = !_favourite;
});
},
child: _favourite? TopIcon(icons: Icons.favorite_border, color: Colors.brown.shade300)
:TopIcon(
icons: Icons.favorite, color: Colors.brown.shade300)
)
],
)
],
),
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition( opacity: animation,
child: child);
},
child: _flag == true ? Center(
key: const Key('1'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/starcup.png'),
),
),
):Center(
key: const Key('2'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/greeen.png'),
),
),
),
),
Positioned(
child:
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: TopIcon(
icons: Icons.car_crash_outlined, color: Colors.brown.shade300),
),
)),
const Positioned(
child:
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.only(top: 330.0, left: 14),
child: Text('\$ 5.99',
style: TextStyle(
fontSize: 27,
fontWeight: FontWeight.bold
),
),
),
))
],
),
);
}
}
and lastly, the lower area
class LowerArea extends StatefulWidget {
final AnimationController controller;
final Animation<Offset> anime;
bool rep;
LowerArea({Key? key, required this.controller, required this.anime, required this.rep}) : super(key: key);
#override
State<LowerArea> createState() => _LowerAreaState();
}
class _LowerAreaState extends State<LowerArea> {
bool _bigger = true;
bool _fade = true;
void move(){
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.all(1.h),
child: const Text('Tall Frappuccino',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.only(right: 5.h),
child: const Text('Swipe Down',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.all(2.h),
child: const Text('Pickup',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
)
],
),
),
SlideTransition(
position: widget.anime,
child: AnimatedContainer(
// height: 11.h,
width: _bigger ? 35.h: 80.h,
duration: const Duration(milliseconds: 500),
child: Stack(
fit: StackFit.loose,
children: [
Center(child: Image.asset('assets/images/baggie.png')),
Center(
child: Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: GestureDetector(
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
widget.rep = !widget.rep;
print('this is fade $_fade ');
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: _fade? Container(
key: Key('1'),
height: 8.h,
width: 7.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(0.3.h),
child: Icon(
Icons.lock_outline,
color: Colors.white54,
size: 2.5.h,
),
),
Icon(
Icons.arrow_drop_down,
color: Colors.white12,
size: 3.h,
),
],
),
):null,
),
),
),
)
],
),
),
)
],
);
}
use provider pacakges https://pub.dev/packages/provider
Create a class that inherits the functions of ChangeNotifyer to create a flag to control and create a setter.
provider class
class StateController extends ChangeNotifier{
bool _req = false;
bool get req => _req; //getter
setReqValue(){
_req = !_req;
notifyListener();
}
}
Declare the provider class in the main function. You can change the location of the declaration according to Wiget tree, but first declare it in main
Change main.dart
void main(){
runApp(
Multiprovider(
providers: [
ChangeNotifierProvider(create: (_) => StateController()),
],
child: HomePage(),
)
);
}
The UI is updated by notifyListener().
change UI
child: context.watch<StateController>().req == true ? Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
Change State
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
context.read<StateController>().setReqValue();
print('this is fade $_fade ');
});
},
I am too lazy to try understand your code. But if you want to update state of the page after you pop from Navigation to it.
In page you want to update
Navigation.push(context, /* Page that will change something */)
// Future will trigger then you return to this page.
.then((_) => setState(() {}))

LateInitializationError: Field 'animationController' has not been initialized even using initState()

I am trying to build a login/signup page by using SingleTickerProviderStateMixin but I am getting errors on AnimationController. How I can solve it, Even try to use nullable? but I have to check! on operation. At that time I also get errors. Please help me to find out any solutions
class log2 extends StatefulWidget {
const log2({Key? key}) : super(key: key);
#override
State<log2> createState() => _log2State();
}
class _log2State extends State<log2> with SingleTickerProviderStateMixin {
bool islogin = true;
Animation<double>? containSize;
late AnimationController animationController;
Duration duration = Duration(milliseconds: 270);
#override
Widget build(BuildContext context) {
double mobHeight = MediaQuery.of(context).size.height;
double mobWidth = MediaQuery.of(context).size.width;
TextEditingController emailController = TextEditingController(),
passController = TextEditingController();
initState() {
super.initState();
animationController =
AnimationController(vsync: this, duration: duration);
SystemChrome.setEnabledSystemUIOverlays([]);
}
//For Ticker Screen
containSize = Tween<double>(begin: mobHeight * 0.1, end: mobWidth * 0.1)
.animate(
CurvedAnimation(parent: animationController, curve: Curves.linear));
#override
void dispose() {
animationController.dispose();
super.dispose();
}
Build Widget:
return SafeArea(
child: Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.only(top: 50),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Welcome",
style: TextStyle(color: Colors.black, fontSize: 25),
),
SizedBox(
height: 40,
),
Transform.rotate(
angle: pi / 4,
child: Image.asset(
"images/computerdesk.png",
fit: BoxFit.cover,
height: mobHeight / 3,
width: mobWidth / 0.2,
),
),
AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return buiderSignupContainer();
},
),
],
),
),
Positioned(
child: Image.asset("images/ball.png"),
),
],
),
),
);
}
2nd stack widget:
Widget buiderSignupContainer() {
double mobheight = MediaQuery.of(context).size.height;
return Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
child: Container(
width: double.infinity,
height: containSize?.value,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(30),
topLeft: Radius.circular(30),
),
color: Colors.black.withAlpha(50),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Don't Have a Account Yet?"),
VerticalDivider(width: 5),
Text(
"Sign Up",
style: TextStyle(color: Colors.red),
),
],
),
),
onTap: () {
setState(() {
islogin = !islogin;
});
},
),
);
}
You put initState and dispose methods to wrong block. Change it to:
class _log2State extends State<log2> with SingleTickerProviderStateMixin { bool islogin = true; Animation<double>? containSize; late AnimationController animationController; Duration duration = Duration(milliseconds: 270);
bool islogin = true;
Animation<double>? containSize;
late AnimationController animationController;
Duration duration = Duration(milliseconds: 270);
#override
void initState() {
super.initState();
animationController = AnimationController(vsync: this, duration: duration);
SystemChrome.setEnabledSystemUIOverlays([]);
//For Ticker Screen
containSize = Tween<double>(begin: mobHeight * 0.1, end: mobWidth * 0.1).animate(CurvedAnimation(parent: animationController, curve: Curves.linear));
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
double mobHeight = MediaQuery.of(context).size.height;
double mobWidth = MediaQuery.of(context).size.width;
TextEditingController emailController = TextEditingController() passController = TextEditingController();
return SafeArea(
child: Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.only(top: 50),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Welcome",
style: TextStyle(color: Colors.black, fontSize: 25),
),
SizedBox(
height: 40,
),
Transform.rotate(
angle: pi / 4,
child: Image.asset(
"images/computerdesk.png",
fit: BoxFit.cover,
height: mobHeight / 3,
width: mobWidth / 0.2,
),
),
AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return buiderSignupContainer();
},
),
],
),
),
Positioned(
child: Image.asset("images/ball.png"),
),
],
),
),
);
}

Ads overlapping bottom navigation in flutter

i have setup banner ads in flutter and those are overlapping the bottom navigation bar
I want to display ads below that bottom navigation bar,
is there any way that i can add a margin below the bottom navigation bar ?
i have implemented ads in home.dart (mainpage)
import 'package:provider/provider.dart';
import '../../ui/widgets/bottom_nav_bar.dart';
import '../../core/utils/theme.dart';
import 'search_page.dart';
import 'category.dart';
import 'main_page.dart';
import 'settings.dart';
import 'package:flutter/material.dart';
import 'package:firebase_admob/firebase_admob.dart';
import 'for_you.dart';
const String AD_MOB_APP_ID = 'ca-app-pub-3940256099942544~3347511713';
const String AD_MOB_TEST_DEVICE = 'DEC3010B2445165B43EB949F5D97D0F8 - run ad then check device logs for value';
const String AD_MOB_AD_ID = 'ca-app-pub-3940256099942544/6300978111';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
BannerAd _bannerAd;
static final MobileAdTargetingInfo targetingInfo = new MobileAdTargetingInfo(
testDevices: <String>[AD_MOB_TEST_DEVICE],
);
BannerAd createBannerAd() {
return new BannerAd(
adUnitId: AD_MOB_AD_ID,
size: AdSize.banner,
targetingInfo: targetingInfo,
);
}
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
int _selectedIndex = 0;
final PageController _pageController = PageController();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
#override
Widget build(BuildContext context) {
final stateData = Provider.of<ThemeNotifier>(context);
final ThemeData state = stateData.getTheme();
FirebaseAdMob.instance.initialize(appId: AD_MOB_APP_ID);
_bannerAd = createBannerAd()..load()..show();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: false,
backgroundColor: state.primaryColor,
elevation: 0,
title: Text(
'RevWalls',
style: state.textTheme.headline,
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: state.textTheme.body1.color,
),
onPressed: () => showSearch(
context: context, delegate: WallpaperSearch(themeData: state)),
)
],
),
body: Container(
color: state.primaryColor,
child: PageView(
controller: _pageController,
physics: BouncingScrollPhysics(),
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: <Widget>[
MainBody(),
Category(),
ForYou(),
SettingsPage(),
],
),
),
bottomNavigationBar: BottomNavyBar(
selectedIndex: _selectedIndex,
unselectedColor: state.textTheme.body1.color,
onItemSelected: (index) {
_pageController.jumpToPage(index);
},
selectedColor: state.accentColor,
backgroundColor: state.primaryColor,
showElevation: false,
items: [
BottomNavyBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavyBarItem(
icon: Icon(Icons.category),
title: Text('Subreddits'),
),
BottomNavyBarItem(
icon: Icon(Icons.phone_android),
title: Text('Exact Fit'),
),
BottomNavyBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
);
}
Widget oldBody(ThemeData state) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: state.primaryColor,
elevation: 4,
title: Text(
'reWalls',
style: state.textTheme.headline,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search, color: state.accentColor),
onPressed: () {
showSearch(
context: context,
delegate: WallpaperSearch(themeData: state));
},
)
],
floating: true,
pinned: _selectedIndex == 0 ? false : true,
snap: false,
centerTitle: false,
),
];
},
body: Container(
color: state.primaryColor,
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: <Widget>[
MainBody(),
Category(),
ForYou(),
SettingsPage(),
],
),
),
);
}
}
and here is the bottom navigation bar -
library bottom_navy_bar;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class BottomNavyBar extends StatelessWidget {
final int selectedIndex;
final double iconSize;
final Color backgroundColor, selectedColor, unselectedColor;
final bool showElevation;
final Duration animationDuration;
final List<BottomNavyBarItem> items;
final ValueChanged<int> onItemSelected;
BottomNavyBar(
{Key key,
this.selectedIndex = 0,
this.showElevation = true,
this.iconSize = 20,
this.backgroundColor,
this.selectedColor,
this.unselectedColor,
this.animationDuration = const Duration(milliseconds: 250),
#required this.items,
#required this.onItemSelected}) {
assert(items != null);
assert(items.length >= 2 && items.length <= 5);
assert(onItemSelected != null);
}
Widget _buildItem(BottomNavyBarItem item, bool isSelected) {
return AnimatedContainer(
width: isSelected ? 120 : 50,
height: double.maxFinite,
duration: animationDuration,
decoration: BoxDecoration(
color: isSelected ? selectedColor.withOpacity(0.2) : backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
),
alignment: Alignment.center,
child: ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8),
child: IconTheme(
data: IconThemeData(
size: iconSize,
color: isSelected ? selectedColor : unselectedColor),
child: item.icon,
),
),
isSelected
? DefaultTextStyle.merge(
style: TextStyle(
fontSize: 16,
color: selectedColor,
fontWeight: FontWeight.bold),
child: item.title,
)
: SizedBox.shrink()
],
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Container(
color: backgroundColor,
child: Container(
width: double.infinity,
height: 55,
padding: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: items.map((item) {
var index = items.indexOf(item);
return GestureDetector(
onTap: () {
onItemSelected(index);
},
child: _buildItem(item, selectedIndex == index),
);
}).toList(),
),
),
);
}
}
class BottomNavyBarItem {
final Icon icon;
final Text title;
BottomNavyBarItem({
#required this.icon,
#required this.title,
}) {
assert(icon != null);
assert(title != null);
}
}
Please help
adding builder like this will solve the problem
var paddingBottom = 60.0;
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
//
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'World General info',
//theme: ThemeData(primarySwatch: Colors.cyan,),
theme: ThemeData(
primaryColor: Colors.cyan,
accentColor: Colors.green,
textTheme: TextTheme(bodyText2: TextStyle(color: Colors.purple)),
),
home: MyHomePage(title: 'World General Info'),
builder: (BuildContext context, Widget child) {
return new Padding(
child: child,
padding: EdgeInsets.only(bottom: paddingBottom),
);
}
);
}
}
You can use the MediaQuery.of(context) for that.
Wrap the whole Code inside a Container of height: MediaQuery.of(context).size.height - 60 . (the height of ad)
Column(
children: [
Container(
height: MediaQuery.of(context).size.height - 60,
child: HomePage(),
),
BannerAd(),
]
);
Found answer myself
We can use this to set margin in a container with other things like height, width
This code will add margin to bottom of bottom nav bar, as per my need i want to show ads below navbar so this solves my problem
margin: const EdgeInsets.only(bottom: 50)
Add this to your scaffold
bottomNavigationBar: Container(
height: 50.0,
color: Colors.white,
),
Think Simplest bro ... wrap your column ( mainAxisSize : MainAxisSize.min )
Scaffold(
appBar: _appBar,
body: _body,
bottomNavigationBar: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: Colors.amber,
height: 70,
child: Center(child: Text('Banner'))),
BottomNavigationBar(
items: const [
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add'),
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add'),
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add')
],
)
]))

Flutter highlight current selected nav item

I have created a starter kit in flutter for a personal project.
Everything is working fine but I have an iisue where I'm unable to highlight the current selected item in the drawer.
I'm abit lost on where I chould be put the code that determins the current selected item.
Below is my code!
class _MdDrawerState extends State<MdDrawer>
with SingleTickerProviderStateMixin<MdDrawer> {
final _animationDuration = const Duration(milliseconds: 350);
AnimationController _animationController;
Stream<bool> isDrawerOpenStream;
StreamController<bool> isDrawerOpenStreamController;
StreamSink<bool> isDrawerOpenSink;
.....
#override
void dispose() {
_animationController.dispose();
isDrawerOpenStreamController.close();
isDrawerOpenSink.close();
super.dispose();
}
void onIconPressed() {
final animationStatus = _animationController.status;
final isAnimationCompleted = animationStatus == AnimationStatus.completed;
.....
}
#override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isDrawerOpenStream,
builder: (context, isLeftDrawerOpenedAsync) {
return AnimatedPositioned(
duration: _animationDuration,
top: 0,
bottom: 0,
left: isLeftDrawerOpenedAsync.data ? 0 : -screenWidth,
right: isLeftDrawerOpenedAsync.data ? 0 : screenWidth - 45,
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
color: Theme.of(context).backgroundColor,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(
height: 30,
),
ListTile(
title: Text('First - Last',
style: Theme.of(context).textTheme.headline),
subtitle: Text('something#gmail.com',
style: Theme.of(context).textTheme.subhead),
leading: CircleAvatar(
child: Icon(
Icons.perm_identity,
color: Theme.of(context).iconTheme.color,
),
radius: 40,
),
),
Divider(
height: 30,
),
MdNavItem(
icon: Icons.home,
title: 'Home',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.HomeClickedEvent);
},
),
MdNavItem(
icon: Icons.account_box,
title: 'Account',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.AccountClickedEvent);
},
),
MdNavItem(
icon: Icons.shopping_basket,
title: 'Orders',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.OrderClickedEvent);
},
),
MdNavItem(
icon: Icons.card_giftcard,
title: 'Wishlist',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.WishlistClickedEvent);
},
),
Divider(
height: 30,
),
MdNavItem(
icon: Icons.settings,
title: 'Settings',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.SettingsClickedEvent);
},
),
MdNavItem(
icon: Icons.exit_to_app,
title: 'Logout',
),
Divider(
height: 45,
),
],
),
],
),
),
),
......
],
),
);
},
);
}
}
And the MdNavItem class
class MdNavItem extends StatelessWidget {
final IconData icon;
final String title;
final Function onTap;
const MdNavItem({this.icon, this.title, this.onTap});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Container(
color: Theme.of(context).backgroundColor,
child: Row(
children: <Widget>[
Icon(
icon,
size: 25,
color: Theme.of(context).iconTheme.color,
),
SizedBox(
width: 20,
),
Text(
title,
style: Theme.of(context).textTheme.headline,
),
],
),
),
),
);
}
}
Edit:
First Way to do:
Add this code to your Drawer:
class _MdDrawerState extends State<MdDrawer>
with SingleTickerProviderStateMixin<MdDrawer> {
final _animationDuration = const Duration(milliseconds: 350);
AnimationController _animationController;
Stream<bool> isDrawerOpenStream;
StreamController<bool> isDrawerOpenStreamController;
StreamSink<bool> isDrawerOpenSink;
final List<bool> isTaped = [true, false, false, false, false]; // the first is true because when the app
//launch the home needs to be in red(or the
//color you choose)
void changeHighlight(int index){
for(int indexTap = 0; indexTap < isTaped.length; indexTap++) {
if (indexTap == index) {
isTaped[index] = true; //used to change the value of the bool list
} else {
isTaped[indexTap] = false;
}
}
}
.....
#override
void dispose() {
_animationController.dispose();
isDrawerOpenStreamController.close();
isDrawerOpenSink.close();
super.dispose();
}
void onIconPressed() {
final animationStatus = _animationController.status;
final isAnimationCompleted = animationStatus == AnimationStatus.completed;
.....
}
#override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isDrawerOpenStream,
builder: (context, isLeftDrawerOpenedAsync) {
return AnimatedPositioned(
duration: _animationDuration,
top: 0,
bottom: 0,
left: isLeftDrawerOpenedAsync.data ? 0 : -screenWidth,
right: isLeftDrawerOpenedAsync.data ? 0 : screenWidth - 45,
child: Row(
children: <Widget>[
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
color: Theme.of(context).backgroundColor,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(
height: 30,
),
ListTile(
title: Text('First - Last',
style: Theme.of(context).textTheme.headline),
subtitle: Text('something#gmail.com',
style: Theme.of(context).textTheme.subhead),
leading: CircleAvatar(
child: Icon(
Icons.perm_identity,
color: Theme.of(context).iconTheme.color,
),
radius: 40,
),
),
Divider(
height: 30,
),
MdNavItem(
wasTaped: isTaped[0],
icon: Icons.home,
title: 'Home',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.HomeClickedEvent);
changeHighlight(0);
},
),
MdNavItem(
wasTaped: isTaped[1],
icon: Icons.account_box,
title: 'Account',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.AccountClickedEvent);
changeHighlight(1);
},
),
MdNavItem(
wasTaped: isTaped[2],
icon: Icons.shopping_basket,
title: 'Orders',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.OrderClickedEvent);
changeHighlight(2);
},
),
MdNavItem(
wasTaped: isTaped[3],
icon: Icons.card_giftcard,
title: 'Wishlist',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.WishlistClickedEvent);
changeHighlight(3);
},
),
Divider(
height: 30,
),
MdNavItem(
wasTaped: isTaped[4],
icon: Icons.settings,
title: 'Settings',
onTap: () {
onIconPressed();
BlocProvider.of<MdNavBloc>(context)
.add(NavigationEvents.SettingsClickedEvent);
changeHighlight(4);
},
),
MdNavItem(
icon: Icons.exit_to_app,
title: 'Logout',
),
Divider(
height: 45,
),
],
),
],
),
),
),
......
],
),
);
},
);
}
}
And this to your MdNavItem:
class MdNavItem extends StatelessWidget {
final IconData icon;
final String title;
final Function onTap;
final bool wasTaped; //receiving the bool value (if was taped or not)
const MdNavItem({this.icon, this.title, this.onTap, this.wasTaped});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Container(
color: Theme.of(context).backgroundColor,
child: Row(
children: <Widget>[
Icon(
icon,
size: 25,
color: wasTaped ? Colors.red : Theme.of(context).iconTheme.color, //the condition to change the color
),
SizedBox(
width: 20,
),
Text(
title,
style: wasTaped ? TextStyle(
color: Colors.red,
) : Theme.of(context).textTheme.headline,
),
],
),
),
),
);
}
}
Old answer, Second way:
First Screen, where PageView is placed:
class FirstScreen extends StatelessWidget {
final PageController pageController = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Kit App'),
),
drawer: CustomDrawer(
pageController: pageController,
),
body: PageView(
controller: pageController,
physics: NeverScrollableScrollPhysics(), //to prevent scroll
children: <Widget>[
HomeScreen(),
AccountScreen(), //your pages
OrdersScreen(),
WishListScreen(),
],
),
);
}
}
The CustomDrawer:
class CustomDrawer extends StatelessWidget {
CustomDrawer({#required this.pageController});
final PageController pageController;
#override
Widget build(BuildContext context) {
return Drawer(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 20),
child: Column(
children: <Widget>[
DrawerItem(
onTap: (){
Navigator.pop(context); //to close the drawer
pageController.jumpToPage(0);
},
leading: Icons.home,
title: 'Home',
index: 0,
controller: pageController,
),
DrawerItem(
onTap: (){
Navigator.pop(context);
pageController.jumpToPage(1);
},
leading: Icons.account_box,
title: 'Account',
index: 1,
controller: pageController,
),
DrawerItem(
onTap: (){
Navigator.pop(context);
pageController.jumpToPage(2);
},
leading: Icons.shopping_cart,
title: 'Orders',
index: 2,
controller: pageController,
),
DrawerItem(
onTap: (){
Navigator.pop(context);
pageController.jumpToPage(3);
},
leading: Icons.card_travel,
title: 'Wishlist',
index: 3,
controller: pageController,
),
],
),
),
);
}
}
and the DrawerItem, where the condition to change the color of the items is placed:
class DrawerItem extends StatelessWidget {
DrawerItem({
#required this.onTap,
#required this.leading,
#required this.title,
#required this.index,
#required this.controller,
});
final VoidCallback onTap;
final IconData leading;
final String title;
final int index;
final PageController controller;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: ListTile(
leading: Icon(
leading,
color: controller.page.round() == index ? Colors.red : Colors.grey,
),
title: Text(
title,
style: TextStyle(
color: controller.page.round() == index ? Colors.red : Colors.grey,
),
),
),
);
}
}
Result:
Now you just need to implement this on your code.

Flutter Backdrop Animation

I'm working a little bit with Flutter and I understand everything except Animations (i never liked working with animations).
I've tried to implement a Backdrop in my Flutter app using this Flutter Demo. Implementing the Backdrop is easy.
I stuck on implementing the navigation of the Backdrop which let it slide down and up by the hamburger button.
I have read the Animations in Flutter Tutorial. I understood the basics of animations (controller, animation etc.). But in this Backdrop example, it is a little bit different.
Can someone explain to me this case step by step? Thanks.
I managed to reach a solution combining the information of the following links. Please note that I’m not an expert in Flutter (neither animations). That being said, suggestions and corrections are really appreciated.
Backdrop implementation:
https://github.com/material-components/material-components-flutter-codelabs/blob/104-complete/mdc_100_series/lib/backdrop.dart
Animation:
https://medium.com/#vigneshprakash15/flutter-image-rotate-animation-6b6eaed7fb33
After creating your backdrop.dart file, go to the _BackdropTitle class and edit the part where you define the IconButton for the menu and close icons. You have to perform a rotation in the Opacity item that contains the Icon:
icon: Stack(children: <Widget>[
new Opacity(
opacity: new CurvedAnimation(
parent: new ReverseAnimation(animation),
curve: const Interval(0.5, 1.0),
).value,
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
child: new Icon(Icons.close),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * -6.3,
child: _widget,
);
},
),
),
new Opacity(
opacity: new CurvedAnimation(
parent: animation,
curve: const Interval(0.5, 1.0),
).value,
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
child: new Icon(Icons.menu),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * 6.3,
child: _widget,
);
},
),
),
],
I had to use a negative value in the angle of the close transformation in order to rotate it in the same direction of the menu animation.
And here is the full code:
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'menu.dart';
const double _kFlingVelocity = 2.0;
class Backdrop extends StatefulWidget {
final MenuCategory currentCategory;
final Widget frontLayer;
final Widget backLayer;
final Widget frontTitle;
final Widget backTitle;
const Backdrop({
#required this.currentCategory,
#required this.frontLayer,
#required this.backLayer,
#required this.frontTitle,
#required this.backTitle,
}) : assert(currentCategory != null),
assert(frontLayer != null),
assert(backLayer != null),
assert(frontTitle != null),
assert(backTitle != null);
#override
_BackdropState createState() => _BackdropState();
}
class _BackdropState extends State<Backdrop>
with SingleTickerProviderStateMixin {
final GlobalKey _backdropKey = GlobalKey(debugLabel: 'Backdrop');
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 300),
value: 1.0,
vsync: this,
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
void didUpdateWidget(Backdrop old) {
super.didUpdateWidget(old);
if (widget.currentCategory != old.currentCategory) {
_toggleBackdropLayerVisibility();
} else if (!_frontLayerVisible) {
_controller.fling(velocity: _kFlingVelocity);
}
}
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
const double layerTitleHeight = 48.0;
final Size layerSize = constraints.biggest;
final double layerTop = layerSize.height - layerTitleHeight;
Animation<RelativeRect> layerAnimation = RelativeRectTween(
begin: RelativeRect.fromLTRB(
0.0, layerTop, 0.0, layerTop - layerSize.height),
end: RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller.view);
return Stack(
key: _backdropKey,
children: <Widget>[
widget.backLayer,
PositionedTransition(
rect: layerAnimation,
child: _FrontLayer(
onTap: _toggleBackdropLayerVisibility,
child: widget.frontLayer,
),
),
],
);
}
#override
Widget build(BuildContext context) {
var appBar = AppBar(
brightness: Brightness.light,
elevation: 0.0,
titleSpacing: 0.0,
title: _BackdropTitle(
animationController: _controller,
onPress: _toggleBackdropLayerVisibility,
frontTitle: widget.frontTitle,
backTitle: widget.backTitle,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
// TODO
},
)
],
);
return Scaffold(
appBar: appBar,
body: LayoutBuilder(builder: _buildStack)
);
}
bool get _frontLayerVisible {
final AnimationStatus status = _controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
void _toggleBackdropLayerVisibility() {
_controller.fling(
velocity: _frontLayerVisible ? -_kFlingVelocity : _kFlingVelocity);
}
}
class _FrontLayer extends StatelessWidget {
const _FrontLayer({
Key key,
this.onTap,
this.child,
}) : super(key: key);
final VoidCallback onTap;
final Widget child;
#override
Widget build(BuildContext context) {
return Material(
elevation: 16.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0)
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
height: 40.0,
alignment: AlignmentDirectional.centerStart,
),
),
Expanded(
child: child,
),
],
),
);
}
}
class _BackdropTitle extends AnimatedWidget {
final AnimationController animationController;
final Function onPress;
final Widget frontTitle;
final Widget backTitle;
_BackdropTitle({
Key key,
this.onPress,
#required this.frontTitle,
#required this.backTitle,
#required this.animationController,
}) : assert(frontTitle != null),
assert(backTitle != null),
assert(animationController != null),
super(key: key, listenable: animationController.view);
#override
Widget build(BuildContext context) {
final Animation<double> animation = this.listenable;
return DefaultTextStyle(
style: Theme.of(context).primaryTextTheme.title,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: Row(children: <Widget>[
// branded icon
SizedBox(
width: 72.0,
child: IconButton(
padding: EdgeInsets.only(right: 8.0),
onPressed: this.onPress,
icon: Stack(children: <Widget>[
new Opacity(
opacity: new CurvedAnimation(
parent: new ReverseAnimation(animation),
curve: const Interval(0.5, 1.0),
).value,
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
child: new Icon(Icons.close),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * -6.3,
child: _widget,
);
},
),
),
new Opacity(
opacity: new CurvedAnimation(
parent: animation,
curve: const Interval(0.5, 1.0),
).value,
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
child: new Icon(Icons.menu),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * 6.3,
child: _widget,
);
},
),
),
]),
),
),
// Here, we do a custom cross fade between backTitle and frontTitle.
// This makes a smooth animation between the two texts.
Stack(
children: <Widget>[
Opacity(
opacity: CurvedAnimation(
parent: ReverseAnimation(animation),
curve: Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: Offset.zero,
end: Offset(0.5, 0.0),
).evaluate(animation),
child: backTitle,
),
),
Opacity(
opacity: CurvedAnimation(
parent: animation,
curve: Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: Offset(-0.25, 0.0),
end: Offset.zero,
).evaluate(animation),
child: frontTitle,
),
),
],
)
]),
);
}
}
You can find a sample demo for backdrop in flutter gallery..which you will get while installing Flutter.

Categories

Resources