I am using AutoRoute with AutoTabsRouter for navigation in my web app. I have a custom Drawer used for navigation. I want my current tab icon and text to be the primary color when on the selected index.
For mobile and tablet view, the drawer pops with every selection and when I open it again the current tab has the primary color as it should. On desktop view this works if I open the app in desktop view and do not resize the screen at all. If I do resize the screen at all, even if I'm still in desktop view after resizing, the selected does not work as expected and the current tab does not have the primary color.
Here is my code for the Drawer :
class CustomDrawer extends StatefulWidget {
final double width;
const CustomDrawer({
required this.width,
Key? key,
}) : super(key: key);
#override
State<CustomDrawer> createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
ScrollController scrollController = ScrollController();
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
#override
Widget build(context) {
final tabsRouter = context.tabsRouter;
return Drawer(
elevation: 0,
backgroundColor: lightThemeCardColor,
child: SingleChildScrollView(
controller: scrollController,
child: Column(
children: [
DrawerHeader(
child: Column(
children: const [
SizedBox(
height: defaultMargin,
),
Icon(
Icons.architecture,
color: lightPrimaryColor,
size: 50,
),
Text(
'Drawer Header',
style: TextStyle(
fontSize: mobileTitleTextSize,
fontWeight: FontWeight.bold,
),
),
],
),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text('H O M E'),
selected: tabsRouter.activeIndex == 0,
selectedColor: lightPrimaryColor,
onTap: () {
// Push to screen
tabsRouter.setActiveIndex(0);
if (widget.width < 1100) {
Navigator.pop(context);
}
},
),
ListTile(
leading: const Icon(Icons.edit),
title: const Text('S E C O N D'),
selected: tabsRouter.activeIndex == 1,
selectedColor: lightPrimaryColor,
onTap: () {
// Push to screen
tabsRouter.setActiveIndex(1);
if (widget.width < 1100) {
Navigator.pop(context);
}
},
),
ListTile(
leading: const Icon(Icons.check),
title: const Text('T H I R D'),
selected: tabsRouter.activeIndex == 2,
selectedColor: lightPrimaryColor,
onTap: () {
// Push to screen
tabsRouter.setActiveIndex(2);
if (widget.width < 1100) {
Navigator.pop(context);
}
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('F O U R T H'),
selected: tabsRouter.activeIndex == 3,
selectedColor: lightPrimaryColor,
onTap: () {
//Push to screen
tabsRouter.setActiveIndex(3);
if (widget.width < 1100) {
Navigator.pop(context);
}
},
),
],
),
),
);
}
}
Here is my code for the desktop home page where CustomDrawer is the first item in body row:
class DesktopHomePage extends StatelessWidget {
const DesktopHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: lightThemeScaffoldColor,
body: Row(
children: [
// Always open drawer
CustomDrawer(width: width),
// Body contents
Expanded(
child: Container(
margin: const EdgeInsets.all(largeMargin),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const MobileTabletTextHeadline(
text: 'Some widget',
),
SizedBox(
height: 300,
child: AspectRatio(
aspectRatio: 4,
child: SizedBox(
width: double.infinity,
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
),
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.all(defaultPadding),
child: Container(color: lightThemeCardColor),
);
}),
),
),
),
),
// Some other widget
const MobileTabletTextHeadline(
text: 'Other Widget',
),
SizedBox(
height: 300,
child: AspectRatio(
aspectRatio: 4,
child: SizedBox(
width: double.infinity,
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: 4,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
),
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.all(defaultPadding),
child: Container(color: lightThemeCardColor),
);
}),
),
),
),
),
],
),
),
),
),
],
),
);
}
}
(All desktop views have the same layout)
And here is my code where I set up AutoTabsRouter:
class NavPage extends StatelessWidget {
const NavPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return AutoTabsRouter(
routes: const [
HomeRouter(),
SecondRouter(),
ThirdRouter(),
FourthRouter(),
],
builder: (context, child, animation) {
return Scaffold(
appBar:
MediaQuery.of(context).size.width < 1100 ? mobileAppBar : null,
backgroundColor: lightThemeScaffoldColor,
body: child,
drawer: CustomDrawer(width: width),
);
},
);
}
}
I've tried using using setState after setting the current index but that does not help. What can I change the Drawer in a way that reflects the changes in desktop view after window resize?
Related
I'm following this tutorial on youtube (https://www.youtube.com/watch?v=MW-KVmnXuiE) and at 01:27, when I tried to reload my emulator, the BottomAppBar didn't show up. There weren't any errors occurred though.
This is my main.dart file
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/screens/home.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Perspective PageView',
theme: ThemeData(
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomAppBar(
child: Row(
children: [
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
],
),
),
);
}
}
And this is my home.dart file
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('AppBar'),
leading: IconButton(
onPressed: () {},
icon: const Icon(Icons.search_outlined),
),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_vert),
),
],
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 92.5,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 35),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
}),
),
),
),
),
const SizedBox(height: 2.0),
////Your Playlist of the week text
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
And this is how my emulator looked like when I reloaded it.
As you can see, the BottomAppBar isn't showing up.
As far as I read your code, the HomePage class didn't get called.
Update the Scaffold inside your MyHomePage class with the BottomAppBar from your HomePage class.
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
bottomNavigationBar: BottomAppBar(
child: Row(
children: [
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
],
),
),
appBar: AppBar(
centerTitle: true,
title: const Text('AppBar'),
leading: IconButton(
onPressed: () {},
icon: const Icon(Icons.search_outlined),
),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_vert),
),
],
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 92.5,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 35),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
}),
),
),
),
),
const SizedBox(height: 2.0),
////Your Playlist of the week text
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
),
),
);
}
}
It's because your are not using the widget HomePage anywhere.
You can add the MyHomePage widget as the body of the scaffold widget in the HomePage, like so:
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomAppBar(
child: Row(
children: [
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
IconButton(onPressed: () {}, icon: const Icon(Icons.home)),
],
),
),
body: MyHomePage(),
);
}
}
And then your App widget would look like:
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Perspective PageView',
theme: ThemeData(
useMaterial3: true,
),
home: const HomePage(),
);
}
}
This question already has answers here:
Flutter: "RenderFlex children have non-zero flex but incoming height constraints are unbounded"
(8 answers)
Closed 23 days ago.
I want to have a GridView.builder inside another TabBar Widget of SingleChildScrollView and I got this error.
======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
I tried to put the GridView in a Container and give a specific height, it doesn't make an error but the Grid was cut off.
This is my SingleChildScrollView Widget code
import ...
class CommunityPage extends StatefulWidget {...}
class _CommunityPageState extends State<CommunityPage> {
ScrollController? sc;
bool check = false;
Future<void> _copyToClipboard() async {...}
#override
void initState() {...}
#override
void dispose() {...}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
elevation: 0.0,
),
drawer: const SideBar(),
floatingActionButton: check
? FloatingActionButton.small(...)
: null,
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
body: SafeArea(
child: ((this.sc == null) && !this.sc!.hasClients)
? const Center(...)
: SingleChildScrollView(
controller: this.sc!,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Container(...),
const SizedBox(height: largeSpace * 0.8),
Row(...),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 65.0),
child: Container(
height: 43,
decoration: BoxDecoration(...),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: TabBar(
indicator: BoxDecoration(),
labelColor: Colors.white,
unselectedLabelColor: Colors.black,
tabs: const [
Tab(
child: Text(
'EVENTS',
style: TextStyle(
fontSize: 15,
fontFamily: 'assets/fonts/LeferiBase',
),
),
),
Tab(
child: Text(
'MEMBERS',
style: TextStyle(
fontSize: 15,
fontFamily: 'assets/fonts/LeferiBase',
),
),
),
],
),
),
),
),
const SizedBox(height: 20),
const Expanded(
child: TabBarView(children: [
EventsScreen(),
MembersScreen(),
]),
),
],
),
),
),
),
),
);
}
}
And this is my GridView code
import ...
class EventsScreen extends StatefulWidget {
const EventsScreen({super.key});
#override
_EventsScreenState createState() {
return _EventsScreenState();
}
}
class _EventsScreenState extends State<EventsScreen> {
List<Nfts> nfts = [];
bool isLoading = true;
NftsProviders nftsProvider = NftsProviders();
Future initNfts() async {
nfts = await nftsProvider.getNfts();
}
#override
void initState() {
super.initState();
initNfts().then((_) {
setState(() {
isLoading = false;
});
});
}
#override
Widget build(BuildContext context) {
return isLoading
? const Center(
child: CircularProgressIndicator(),
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: nfts.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NFTDetailPage()),
);
},
child: CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(nfts[index].meta_image),
),
),
);
},
),
);
}
}
You are using expanded inside SingleChildScrollView which is giving you error.
Try removing the expanded widget wrap from tabView.
Change this
Expanded(
child: TabBarView(children: [
EventsScreen(),
MembersScreen(),
]),
to
TabBarView(children: [
EventsScreen(),
MembersScreen(),
]
I am creating an app in flutter, I have got some issue in drawer. I want to change the color of the selected drawer item.Here is my full code it looks fine to me but its not working for me... please help me find out what i am doing wrong
class _DrawerClassState extends State<DrawerClass> {
Here is my full code it looks fine to me but its not working for me... please help me find out what i am doing wrong
List<String> menuStrings = [
'HOME',
'NOTIFICATIONS',
'PARTNERS',
'LOCATIONS',
'FEEDBACK',
'CONTACT US',
'AWARDS'
];
Here is my full code it looks fine to me but its not working for me... please help me find out what i am doing wrong
List menuScreens = [
HomeScreen(),
Notifications(),
Partners(),
Locations(),
FeedbackScreen(),
const ContactUs(),
Awards()
];
Here is my full code it looks fine to me but its not working for me... please help me find out what i am doing wrong
List<bool> isHighlighted = [false, false, false, false, true, false, false];
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.black,
),
child: Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
drawerTop("HI USER"),
ListView.builder(
shrinkWrap: true,
itemCount: menuScreens.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
for (int i = 0; i < isHighlighted.length; i++) {
setState(() {
if (index == i) {
isHighlighted[index] = true;
} else {
//the condition to change the highlighted item
isHighlighted[i] = false;
}
});
}
},
child: drawerItems(
context,
menuStrings[index],
menuScreens[index],
isHighlighted[index] ? Colors.amber : Colors.white,
),
);
},
),
],
),
),
);
}
}
Here is the method drawerItems() to build drawer items
drawerItems(BuildContext context, String title, path, Color color) {
return Padding(
padding: EdgeInsets.only(
top: 0.0, left: MediaQuery.of(context).size.width * 0.1),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
GestureDetector(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: ((context) => path)));
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
title,
style: TextStyle(
color: color,
fontFamily: "Raleway Reg",
fontSize: 23,
letterSpacing: 2),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Divider(
thickness: 1,
color: Colors.white,
height: 3,
),
),
]));
}
Update your drawerItems
drawerItems(BuildContext context, String title, path, Color color) {
return Container(
color: color,
padding: EdgeInsets.only(
top: 0.0, left: MediaQuery.of(context).size.width * 0.1),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
GestureDetector(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: ((context) => path)));
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text(
title,
style: TextStyle(
color: color,
fontFamily: "Raleway Reg",
fontSize: 23,
letterSpacing: 2),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: const Divider(
thickness: 1,
color: Colors.white,
height: 3,
),
),
]));
}
the root widget that is returned from the method, change it from Padding to Container and give it a color
I think it's a very complex solution to solve this simple task in Flutter.
I suggest my solution:
Publish it on my git:
https://github.com/igdmitrov/flutter-drawer
Create a new Abstract Page State:
abstract class MyPageState<T extends StatefulWidget> extends State {
List<Widget> drawerItems(BuildContext context) {
return menuItems
.map(
(item) => ListTile(
leading: const Icon(Icons.my_library_books),
title: Text(
item['menuName'] as String,
style: TextStyle(
color: isHighlighted[menuItems.toList().indexOf(item)]
? Colors.amber
: Colors.grey),
),
onTap: () {
isHighlighted = isHighlighted.map((mark) => false).toList();
isHighlighted[menuItems.toList().indexOf(item)] = true;
Navigator.of(context).push(MaterialPageRoute(
builder: ((context) => item['route'] as Widget)));
},
),
)
.toList();
}
}
Create drawer.dart file with code:
final menuItems = {
{'menuName': 'HOME', 'route': const HomeScreen()},
{'menuName': 'NOTIFICATIONS', 'route': const Notifications()},
};
List<bool> isHighlighted = [false, false];
And create pages:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
MyPageState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends MyPageState<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Main page'),
),
drawer: Drawer(
child: ListView(
children: [
const Text("HI USER"),
...drawerItems(context),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'New app',
),
],
),
),
);
}
}
And Notifications page:
class Notifications extends StatefulWidget {
static String routeName = '/notifications';
const Notifications({Key? key}) : super(key: key);
#override
MyPageState<Notifications> createState() => _NotificationsState();
}
class _NotificationsState extends MyPageState<Notifications> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Notifications'),
),
drawer: Drawer(
child: ListView(
children: [
const Text("HI USER"),
...drawerItems(context),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'Notifications page',
),
],
),
),
);
}
}
i want to implement the sliver app bar as shown in the the 2 pictures given below. After much googling , I fount about the CustomScrollView widget and the SliverAppBar widget but all the tutorials and blogs online about sliver app bars show a simple one where an image disappears into an app bar with a text as title on scrolling. However here what I want to achieve is slightly different and I am having a hard time trying to figure out how to do it. Can anyone help me with it?
You can try this:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController? _scrollController;
bool lastStatus = true;
double height = 200;
void _scrollListener() {
if (_isShrink != lastStatus) {
setState(() {
lastStatus = _isShrink;
});
}
}
bool get _isShrink {
return _scrollController != null &&
_scrollController!.hasClients &&
_scrollController!.offset > (height - kToolbarHeight);
}
#override
void initState() {
super.initState();
_scrollController = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
_scrollController?.removeListener(_scrollListener);
_scrollController?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: 'Horizons Weather',
home: Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
elevation: 0,
backgroundColor: Colors.blueGrey,
pinned: true,
expandedHeight: 275,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: _isShrink
? const Text(
"Profile",
)
: null,
background: SafeArea(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 48),
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 100,
width: 100,
),
),
),
const SizedBox(
height: 16,
),
Text(
"Flipkart",
style: textTheme.headline4,
),
const SizedBox(
height: 8,
),
const Text(
"flipkart.com",
),
const SizedBox(
height: 5,
),
const Text(
"Info about the company",
),
],
),
),
),
actions: _isShrink
? [
Padding(
padding: const EdgeInsets.only(left: 8, right: 12),
child: Row(
children: [
Padding(
padding:
const EdgeInsets.only(left: 8, right: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Flipkart",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Text(
"flipkart.com",
style: TextStyle(
fontSize: 12,
),
),
],
),
),
ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(
headerImage,
fit: BoxFit.cover,
height: 30,
width: 30,
),
),
],
),
),
]
: null,
),
];
},
body: CustomScrollView(
scrollBehavior: const ConstantScrollBehavior(),
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(child: Text("Item: $index")),
);
},
childCount: 50,
),
),
],
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const UserAccountsDrawerHeader(
accountName: Text("Zakaria Hossain"),
accountEmail: Text("zakariaaltime#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.orange,
child: Text(
"A",
style: TextStyle(fontSize: 40.0),
),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.contacts),
title: Text("Contact Us"),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
),
);
}
}
Demo
I have a Stateful widget class with a card, the card has a dropdown and a text field. There is a Floatingactionbutton, I want to create an additional card when ever the floatingactionbutton is pressed. I guess I am supposed to create a list of this widget, but I am not too sure how to go about it.
Here is the code with the cards.
class CustomerCurrentSuppliers extends StatefulWidget {
const CustomerCurrentSuppliers({Key key}) : super(key: key);
#override
_CustomerCurrentSuppliersState createState() => _CustomerCurrentSuppliersState();
}
class _CustomerCurrentSuppliersState extends State<CustomerCurrentSuppliers> {
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
child: Icon(Icons.add),
onPressed: () {
//This is where the code to create additional cards come in ------>
},
),
body: Padding(
padding: const EdgeInsets.only(top: 38.0, right: 10, left: 10),
child: Column(
children: [
Container(
height: 170,
child: Card(
child:
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButtonHideUnderline(
child: FormBuilderDropdown(
name: 'dropdown',
hint: Text("Year"),
isExpanded: true,
items: [
"2018",
"2019",
"2020",
"2021",
].map((option) {
return DropdownMenuItem(
child: Text("$option"),
value: option,
);
}).toList(),
),
),
),
SizedBox(height: 20,),
Padding(
padding: const EdgeInsets.all(8.0),
child: FormBuilderTextField(
name: 'Region',
decoration: InputDecoration(
labelText: "Amount",
border: OutlineInputBorder()),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
),
),
],
),
elevation: 8,
),
),
],
),
),
);
}
}
You didn't provide the full code but I understand the logic you want.
Here is the code.
import 'package:flutter/material.dart';
class CustomerCurrentSuppliers extends StatefulWidget {
const CustomerCurrentSuppliers({Key key}) : super(key: key);
#override
_CustomerCurrentSuppliersState createState() => _CustomerCurrentSuppliersState();
}
class _CustomerCurrentSuppliersState extends State<CustomerCurrentSuppliers> {
int counter=1;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
child: Icon(Icons.add),
onPressed: () {
setState(() { //setState is used to update the UI
counter++;
});
},
),
body: Padding(
padding: const EdgeInsets.only(top: 38.0, right: 20, left: 20),
child: ListView.builder(
itemCount: counter, //updating counter will update the UI with new card
itemBuilder: (context,index){
return Card(
child: Center(
child: Text(
"This is card ${index+1}"
),
),
);
}),
),
);
}
}
You can create List and increment the List when fab pressed.
class CustomerCurrentSuppliers extends StatefulWidget {
#override
_CustomerCurrentSuppliersState createState() =>
_CustomerCurrentSuppliersState();
}
class _CustomerCurrentSuppliersState extends State<CustomerCurrentSuppliers> {
int cardCount = 1;
List<int> cardList = [1];
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
child: Icon(Icons.add),
onPressed: () {
cardCount += 1;
cardList.add(cardCount);
setState(() {});
},
),
body: Padding(
padding: const EdgeInsets.only(top: 38.0, right: 10, left: 10),
child: ListView.builder(
itemCount: cardList.length,
itemBuilder: (content, index) {
return Container(
height: 170,
child: Card(
child: Column(
children: [
Text('FormBuilderDropdown'),
SizedBox(
height: 20,
),
Text('Region')
],
),
elevation: 8,
),
);
})),
);
}
}
Note: You have to handle errors/add more logics when pressing the button otherwise list will increment every time user press the button.