Related
How to make sticky TabBar in this answer.
I Want category section to stick below app bar when I scroll down.
I followed this answer and want to make it sticky for better UX.
enter image description here
#override
Widget build(BuildContext context) {
return Container(
color: mainBackgroundColor,
child: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
backgroundColor: mainBackgroundColor,
appBar: PreferredSize(
preferredSize: Size(double.infinity, 65),
child: AppBarr(isAppBar: isAppBar, label: 'Current Affairs')),
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, value) {
return [
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
searchBar(),
SizedBox(height: mainpadding),
topStoriesText(),
SizedBox(height: mainpadding/2),
topStoriesCard(),
SizedBox(height: mainpadding/2),
],
)),
SliverToBoxAdapter(
child: categoryTab(),
),
];
},
body: Container(
padding: EdgeInsets.only(top: mainpadding/2),
child: TabBarView(
controller: _tabController,
children: [
Stack(
alignment: Alignment.center,
children: [
ListView.builder(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(bottom: mainpadding),
itemCount: isLoadingData?lists.length:lists.length+1,
itemBuilder: (context, index) {
if(isLoadingData==false){
if(index==lists.length){
return Padding(
padding: EdgeInsets.only(
top: mainpadding,
left: mainpadding/2,
right: mainpadding/2,
bottom: mainpadding/2
),
child: AppButton(
text: 'Load More...',
textcolor: whiteColor,
primarycolor: blackColor,
onPressed: (() {
paginatedData();
})),
);
}
}
final users = lists[index];
return all(users);
}
),
isLoadingData?
Container(
color: Color.fromARGB(45, 0, 0, 0),
)
:SizedBox(),
isLoadingData?
Container(
height: 100,
width: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
shape: BoxShape.rectangle,
color: whiteColor,
boxShadow: [
BoxShadow(
offset: Offset(1, 3),
blurRadius: 5,
color: Colors.black.withOpacity(0.14))
],
),
child: Center(child: CircularProgressIndicator(color: blackColor,)),
)
:SizedBox(),
],
),
_buildTabContext(2),
_buildTabContext(200),
_buildTabContext(2),
_buildTabContext(50),
_buildTabContext(50),
_buildTabContext(50),
_buildTabContext(50),
],
),
),
),
),
),
),
);
}
categoryTab(){
return TabBar(
controller: _tabController,
labelColor: blackColor,
splashBorderRadius: BorderRadius.circular(10),
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: mainpadding/2),
indicatorColor: blackColor,
labelStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600),
labelPadding: EdgeInsets.symmetric(vertical: 0,horizontal: mainpadding/1.5),
isScrollable: true,
tabs: [
Tab(text: 'All'),
Tab(text: 'General'),
Tab(text: 'International'),
Tab(text: 'Nation'),
Tab(text: 'Education'),
Tab(text: 'Economy'),
Tab(text: 'Sports'),
Tab(text: 'Tech')
]
);
}
My listview widget is overflowed over another widget like the below screen.
Here is my full code.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:propsoft/utils/dotted_decor.dart';
import '../../utils/app_theme.dart';
import '../../widget/elevated_icon_button_widget.dart';
import '../../widget/helper_utils.dart';
import '../../widget/label_widget.dart';
import 'create_user_logic.dart';
class CreateUserPage extends GetView<CreateUserLogic> {
final logic = Get.find<CreateUserLogic>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppbar(),
body: SafeArea(child: getBodyDetails()),
);
}
Widget getBodyDetails() {
return Column(
children: [
getSearchWidget(),
DefaultTabController(
length: 2,
child: Expanded(
child: Column(
children: [getTabBar(), getTabVarView()],
),
),
),
],
);
}
Widget getTabBar() {
return TabBar(
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 2.0, color: AppTheme.colors.black)),
labelColor: AppTheme.colors.black,
unselectedLabelColor: AppTheme.colors.gray,
indicatorSize: TabBarIndicatorSize.tab,
tabs: const [
Tab(text: "Users"),
Tab(
text: 'Status',
),
],
);
}
Widget getTabVarView() {
return Expanded(
child: TabBarView(
children: [
usersList(),
const Center(
child: Text("Status"),
),
]),
);
}
Widget usersList() {
return Column(
children: [
Expanded(
child: Column(
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: DottedDecoration(
color: AppTheme.colors.darkBlue, shape: Shape.circle),
child: Icon(
Icons.add,
color: AppTheme.colors.darkBlue,
),
),
const SizedBox(
width: 20,
),
PLabel(
text: "Invite New Users",
enumFontWeight: PSFontWeight.bold,
textColor: AppTheme.colors.darkBlue,
)
],
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, position) {
return InkWell(
onTap: () {},
child: Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
child: Row(
children: [
PIconButton(
backgroundColor: AppTheme.colors.lightBlue,
icon: const PLabel(
fontSize: 22,
text: "HT",
),
),
const SizedBox(
width: 16,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
PLabel(text: "My Contact List"),
SizedBox(
height: 4,
),
PLabel(text: "Activated"),
],
)
],
),
),
);
},
itemCount: 10,
),
)
],
),
),
],
);
}
Widget getSearchWidget() {
return Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller.searchController,
onChanged: (query) {
controller.filterSearchResult(query);
},
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4)),
labelText: "Search for a user",
)),
)
],
));
}
AppBar customAppbar() {
return AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: InkWell(
onTap: () {},
child: PLabel(
text: "Save",
fontSize: 18,
textColor: AppTheme.colors.darkBlue,
)),
),
)
],
leading: IconButton(
icon: getSVGImage("assets/images/cross.svg"),
onPressed: () {
Get.back();
},
),
leadingWidth: 40,
title: const PLabel(
text: "Users & Group",
fontSize: 22,
),
backgroundColor: AppTheme.colors.white,
elevation: 0);
}
}
I have worked on your code....its work fine only...
Things changed... instead of PLabel Widget I have used Text Widget and Instead of PIconButton Widget I have used normal Icon Widget.... take my code as reference only...because I have changed your icons because of not having getSVGImage package and some other package.. And attaching image for your reference
Working example:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppbar(),
body: SafeArea(child: getBodyDetails()),
);
}
Widget getBodyDetails() {
return Column(
children: [
getSearchWidget(),
DefaultTabController(
length: 2,
child: Expanded(
child: Column(
children: [getTabBar(), getTabVarView()],
),
),
),
],
);
}
Widget getTabBar() {
return const TabBar(
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 2.0, color: Colors.black)),
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
indicatorSize: TabBarIndicatorSize.tab,
tabs: [
Tab(text: "Users"),
Tab(
text: 'Status',
),
],
);
}
Widget getTabVarView() {
return Expanded(
child: TabBarView(
children: [
usersList(),
const Center(
child: Text("Status"),
),
]),
);
}
Widget usersList() {
return Column(
children: [
Expanded(
child: Column(
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 22, horizontal: 16),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blueAccent, shape: BoxShape.circle),
child: Icon(
Icons.add,
color: Colors.black12,
),
),
const SizedBox(
width: 20,
),
Text(
"Invite New Users",
)
],
),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, position) {
return InkWell(
onTap: () {},
child: Container(
margin:
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
child: Row(
children: [
IconButton(
color: Colors.lightBlue,
icon : const Icon(
Icons.home,
),
onPressed: () { },
),
const SizedBox(
width: 16,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("My Contact List"),
SizedBox(
height: 4,
),
Text("Activated"),
],
)
],
),
),
);
},
itemCount: 10,
),
)
],
),
),
],
);
}
Widget getSearchWidget() {
return Container(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: TextField(
// controller: controller.searchController,
// onChanged: (query) {
// controller.filterSearchResult(query);
// },
decoration: InputDecoration(
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4)),
labelText: "Search for a user",
)),
)
],
));
}
AppBar customAppbar() {
return AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: InkWell(
onTap: () {},
child: Text(
"Save",
)),
),
)
],
leading: IconButton(
icon: Icon(Icons.book),
onPressed: () {
// Get.back();
},
),
leadingWidth: 40,
title: const Text(
"Users & Group",
),
backgroundColor: Colors.white,
elevation: 0);
}
I want to create an web ui in flutter like trello in that I want one horizontal primary scroll and one vertical secondary scroll but I am not able to scroll vertically in column I attach what I want to create and my code and if you still not understand please let me know
Here is my code :-
class PlaningWeekScreenWeb extends StatelessWidget {
const PlaningWeekScreenWeb({Key? key}) : super(key: key);
// Use this for set width of card and row in list
final double cardWidth = 180.0;
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(child: _getSideMenu()),
Container(
height: Get.height,
width: 1,
color: scrollBarColor,
),
Expanded(
child: _getMainScreen(),
flex: 6,
),
],
);
}
Widget _getMainScreen() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ListView.builder(
itemCount: 4,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
itemBuilder: (context, i) {
return Row(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: cardWidth,
child: Row(
children: [
(10.0).addWSpace(),
"29".h3(
size: 25,
weight: FontWeight.w700,
color: GrayColor),
(5.0).addWSpace(),
Expanded(
child: "Sun".h3(
color: GrayColor,
weight: FontWeight.w400,
size: 15)),
(10.0).addWSpace(),
],
),
),
Container(
color: scrollBarColor,
height: 1,
width: cardWidth,
),
SizedBox(
width: cardWidth,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
primary: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
PlaningWeekCard(
device: Device.Web,
onTap: () {},
width: cardWidth,
),
],
),
),
Container(
height: Get.height,
width: 1,
color: scrollBarColor,
),
],
),
),
],
),
],
);
}),
);
}
Widget _getSideMenu() {
return Container(
height: Get.height,
// width: 220,
child: SingleChildScrollView(
padding: EdgeInsets.zero,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Employers".h3(weight: FontWeight.w700, size: 22),
(10.0).addHSpace(),
PerytonSearchField(
hintText: 'Name',
),
(10.0).addHSpace(),
ExpansionTile(
title: Row(
children: [
PerytonCheckBox(val: false),
(5.0).addWSpace(),
Expanded(child: "Codonnier Offieces".expansionTileTittleWeb())
],
),
children: [
_getRow("Norway office"),
_getRow("Surat office"),
_getRow("Bardoli office"),
_getRow("Usa office"),
],
),
(10.0).addHSpace(),
ExpansionTile(
title: Row(
children: [
PerytonCheckBox(val: false),
(5.0).addWSpace(),
Expanded(child: "Codonnier Branches".expansionTileTittleWeb())
],
),
children: [
_getRow("Amsterdam office"),
_getRow("Surat office"),
_getRow("Suart-6 office"),
_getRow("california office"),
],
),
(10.0).addHSpace(),
ExpansionTile(
title: Row(
children: [
PerytonCheckBox(val: false),
(5.0).addWSpace(),
Expanded(
child: "Codonnier sub branches".expansionTileTittleWeb())
],
),
children: [
_getRow("van gogh branch"),
_getRow("Mota varacha branch"),
_getRow("vesu branch"),
_getRow("san francisco office"),
],
),
(10.0).addHSpace(),
"Workers".h3(weight: FontWeight.w700, size: 22),
(10.0).addHSpace(),
PerytonSearchField(
hintText: 'Name',
),
(10.0).addHSpace(),
_getRow("Harsh codonnier"),
(10.0).addHSpace(),
_getRow("Dipak codonnier"),
(10.0).addHSpace(),
_getRow("Subham codonnier"),
(10.0).addHSpace(),
_getRow("ravi codonnier"),
(10.0).addHSpace(),
],
).pSymmetricOnly(horizontal: 10),
),
);
}
Widget _getRow(String name) {
return Row(
children: [
PerytonCheckBox(val: false),
(5.0).addWSpace(),
Expanded(child: name.expansionTileWeb())
],
);
}
}
Here is my card widget:-
class PlaningDayCard extends StatelessWidget {
PlaningDayCard(
{Key? key,
required this.device,
required this.onTap,
required this.width})
: super(key: key);
final Device device;
final double width;
final Function onTap;
#override
Widget build(BuildContext context) {
return ResponsiveWidget(
mobile: _getMobileCard(),
tablet: _getWebCard(),
desktop: _getWebCard(),
device: device);
}
Widget _getWebCard() {
return SizedBox(
width: width - 2,
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white, width: 0.0),
borderRadius: BorderRadius.circular(15),
),
child: Material(
borderRadius: BorderRadius.circular(15),
clipBehavior: Clip.antiAlias,
color: Colors.transparent,
child: InkWell(
onTap: () {
onTap.call();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: "Scrubbing sinks, tubs, showers"
.h2(weight: FontWeight.w700, size: 15),
),
],
),
(6.0).addHSpace(),
Row(
children: [
ImageAssetIconWithColor(
color: GrayColor,
size: 15,
image: ImageIcons.icTriangle,
),
(8.0).addWSpace(),
Expanded(
child: "Main Office".h2(
weight: FontWeight.w500,
color: GrayColor,
size: 14),
),
],
),
(8.0).addWSpace(),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
"09:00—13:00".h2(
weight: FontWeight.w700, color: GrayColor, size: 13),
],
)
],
).pSymmetricOnly(vertical: 5, horizontal: 6),
),
)),
);
}
Widget _getMobileCard() {
return SizedBox(
width: width - 2,
child: Card(
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white, width: 0.0),
borderRadius: BorderRadius.circular(15.sp),
),
child: Material(
borderRadius: BorderRadius.circular(15.sp),
clipBehavior: Clip.antiAlias,
color: Colors.transparent,
child: InkWell(
onTap: () {
onTap.call();
},
child: Column(
children: [],
),
),
)),
);
}
}
Complete Running Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Container(
height: 200,
child: GridView.count(
scrollDirection: Axis.horizontal,
crossAxisCount: 1 ,
children: List.generate(50,(index){
return Container(
child: Card(
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: <Widget>[
Container(
width: 50,
height: 100,
color: Colors.yellowAccent,
),
Container(
width: 50,
height: 100,
color: Colors.blue,
),
Container(
width: 50,
height: 100,
color: Colors.green,
),
Container(
width: 50,
height: 100,
color: Colors.red,
),Container(
width: 50,
height: 100,
color: Colors.yellowAccent,
),
Container(
width: 50,
height: 100,
color: Colors.blue,
),
Container(
width: 50,
height: 100,
color: Colors.green,
),
Container(
width: 50,
height: 100,
color: Colors.red,
)
],
),
color: Colors.amber,
),
);
}),
),
) ),
),
),
);
}
}
Complete Tested Code
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class TrelloExample extends StatefulWidget {
const TrelloExample({Key? key}) : super(key: key);
#override
State<TrelloExample> createState() => _TrelloExampleState();
}
class _TrelloExampleState extends State<TrelloExample> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.only(top: 64.0),
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
},
),
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
scrollDirection: Axis.horizontal,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(
16,
(index) => SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(
right: 16.0, left: index == 0 ? 16.0 : 0.0),
child: Column(
children: List.generate(
index % 2 == 1
? 16
: index % 3 == 1
? 8
: index % 1 == 1
? 4
: 6,
(index) => Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Material(
elevation: 4.0,
child: Container(
padding: const EdgeInsets.all(38.0),
color: Color(
(Random().nextDouble() * 0xFFFFFF).toInt())
.withOpacity(1.0),
child: Text(
"Trello Ticket Item $index",
style: const TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
),
),
),
),
),
),
),
);
}
}
You can achieve this with more than one ways. But I think the simplest way is that make Row as parent widget put all the ListView in Row after wrapping every ListView by Expanded widget.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: ListView.separated(
itemCount: 10,
controller: ScrollController(),
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) => Container(
height: 80,
width: double.infinity,
color: Colors.red,
)),
),
),
const SizedBox(width: 10),
Expanded(
child: ListView.separated(
controller: ScrollController(),
itemCount: 2,
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) => Container(
height: 100,
width: double.infinity,
color: Colors.yellow,
)),
),
),
const SizedBox(width: 10),
Expanded(
child: ListView.separated(
controller: ScrollController(),
itemCount: 6,
separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: ((context, index) => Container(
height: 70,
width: double.infinity,
color: Colors.green,
)),
),
)
],
),
),
);
}
Wrap with SingleChildScrollView over every page you want to scroll.
And also mention the scrollDirection: Axis.vertical.
I hope it works! :)
First must wrap it with a single scroll child view.
Then wrap it in rows,
Then wrap it in a ListView
Use expanded so everything can be seen.
I hope this works for you.
class myScroll extends MaterialScrollBehavior {
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
The SliverAppbar will stretch and zoom if it is in a customScrollView but it does not when it is in NestedScrollView. Only the body of NestedScrollView has a BouncingScrollPhysics, how can the header of NestedScrollView also have the BouncingScrollPhysics so it can stretch and zoom the SliverAppBar.
However, if i add scrollController in the body/inner scrollView then the header SliverAppBar stretches, but then they become two separate scroll views and the header does not scroll up when the body is scrolled. Check the
Screen recording
#override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: 3,
child: NestedScrollView(
physics: BouncingScrollPhysics(),
controller: mainScroller,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
stretch: true,
pinned: true,
floating: false,
elevation: 0,
onStretchTrigger: () {
print('stretch');
return;
},
title: isFlexibleSpaceVisible ? null : Text(widget.name),
expandedHeight: containerHeight - kToolbarHeight,
flexibleSpace: FlexibleSpaceBar(
stretchModes: <StretchMode>[
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
collapseMode: CollapseMode.pin,
background: Container(
color: Theme.of(context).canvasColor,
child: Stack(
children: <Widget>[
Container(
height: headerImageHeight,
width: double.infinity,
decoration: BoxDecoration(
color: kBackgroundColor,
image: DecorationImage(
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black54,
BlendMode.darken,
),
image: NetworkImage(
'https://picsum.photos/seed/${Random().nextInt(100)}/${MediaQuery.of(context).size.width.toInt()}'),
),
),
),
Container(
height: headerSpace,
margin: EdgeInsets.only(
top: headerImageHeight - bringImageUpMargin),
padding: EdgeInsets.only(left: 8, right: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Theme.of(context).canvasColor,
shape: BoxShape.circle),
padding: EdgeInsets.all(6),
child: CircleAvatar(
backgroundColor: kCardColor,
backgroundImage: NetworkImage(
'https://picsum.photos/seed/${Random().nextInt(100)}/200'),
radius: profileImageRadius,
),
),
Expanded(
child: Container(
margin: EdgeInsets.symmetric(
horizontal: 4, vertical: 12),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: <Widget>[
Text(
widget.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700),
),
SizedBox(height: 2),
Wrap(
alignment: WrapAlignment.spaceBetween,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: kPrimaryColor,
borderRadius:
BorderRadius.circular(4)),
child: Text(
widget.skill,
style: TextStyle(
fontSize: 12,
color: Colors.white),
),
),
Container(
padding: EdgeInsets.symmetric(
vertical: 2, horizontal: 6),
decoration: BoxDecoration(
color: kCardColor,
borderRadius:
BorderRadius.circular(4)),
child: Text(
'\$${widget.rate} / ${widget.per}',
style: TextStyle(
color: kPrimaryColor,
fontSize: 12),
),
),
],
),
],
),
),
)
],
),
),
],
),
),
),
),
SliverToBoxAdapter(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
width: 22,
height: 18,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: kSecondaryColor,
),
child: Icon(
Icons.star,
color: Colors.white,
size: 14,
)),
SizedBox(
width: 6,
),
Text('Do you recommend ${widget.name} ?'),
],
),
Row(
children: <Widget>[
Expanded(
child: OutlineButton(
highlightedBorderColor: kSecondaryColor,
color: kSecondaryColor,
borderSide: BorderSide(color: kSecondaryColor),
textColor: kSecondaryColor,
onPressed: () => null,
child: Text('NO'),
),
),
SizedBox(
width: 8,
),
Expanded(
child: RaisedButton(
onPressed: () => null,
child: Text('YES'),
),
),
],
)
],
),
),
),
SliverPersistentHeader(
pinned: true,
delegate: SliverProfileTabs(),
)
];
},
body: ProfileTabsView(data: widget),
),
),
);
}
}
As mentioned in the comment, there is an open bug in GitHub for this.
The reason for this behavior was discussed in this
thread:
The reason the stretch does not happen is because the
_NestedScrollViewCoodinator currently prioritizes the inner scrollable for any overscroll from the user drag. I am thinking we can add a flag
like, NestedScrollView.stretchHeaderSlivers, which will flip that to
send overscroll to the outer scrollable. That part should be easy.
The _NestedScrollViewCoordinator.createOuterBallisticScrollActivity
will need to be refactored since it currently assumes that the outer
scrollable will never overscroll, which may or may not be as easy.
And they have mentioned that this is a huge refactoring and there is no suitable solutions yet:
I've taken a crack at this a few different times and have not found a
suitable solution yet. The NestedScrollView bases much more than I
though on the assumption that the outer scroll view will never
overscroll. I believe this will take a larger refactoring/re-design of
the widget in order to support, maybe work we can schedule for in the
new year. For now, I won't be actively working on this anymore. I am
un-assigning myself for now in case some one else would like to try
solving this in the meantime. :)
Since I'm not able to view your screen recording, I'll just post a sample view of how the bug looks like. It was taken from the GitHub issue thread.
Sample code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
theme: ThemeData.dark(),
home: Home(),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
body: NestedScrollView(
physics: BouncingScrollPhysics(),
headerSliverBuilder: (context, innerScrolled) => <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
context),
sliver: SliverAppBar(
pinned: true,
stretch: true,
title: Text('username'),
expandedHeight: 325,
flexibleSpace: FlexibleSpaceBar(
stretchModes: <StretchMode>[
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: Image.network(
'https://i.imgur.com/QCNbOAo.png',
fit: BoxFit.cover)),
bottom: TabBar(
tabs: <Widget>[Text('test1'), Text('test2')])),
)
],
body: TabBarView(children: [
Center(
child: Builder(
builder: (context) => CustomScrollView(
slivers: <Widget>[
SliverOverlapInjector(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(
context)),
SliverFixedExtentList(
delegate: SliverChildBuilderDelegate(
(_, index) => Text('not working'),
childCount: 100),
itemExtent: 25)
],
),
),
),
Center(child: Text('working'))
])),
));
}
}
Output:
I want to display a image background in my Login Page like cimb niaga mobile in which you can have a dot indicator slide running behind form login.
I want like this image example
this my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
enum AuthMode { LOGIN, SINGUP }
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
// To adjust the layout according to the screen size
// so that our layout remains responsive ,we need to
// calculate the screen height
double screenHeight;
// Set intial mode to login
AuthMode _authMode = AuthMode.LOGIN;
#override
Widget build(BuildContext context) {
screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
body: SingleChildScrollView(
child: Stack(
children: <Widget>[
lowerHalf(context),
upperHalf(context),
_authMode == AuthMode.LOGIN
? loginCard(context)
: singUpCard(context),
pageTitle(),
],
),
),
);
}
Widget loginCard(BuildContext context) {
return Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: screenHeight / 4),
padding: EdgeInsets.only(left: 10, right: 10),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 8,
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: Text(
"Login",
style: TextStyle(
color: Colors.black,
fontSize: 28,
fontWeight: FontWeight.w600,
),
),
),
SizedBox(
height: 15,
),
TextFormField(
decoration: InputDecoration(
labelText: "Your Email", hasFloatingPlaceholder: true),
),
SizedBox(
height: 20,
),
TextFormField(
decoration: InputDecoration(
labelText: "Password", hasFloatingPlaceholder: true),
),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
MaterialButton(
onPressed: () {},
child: Text("Forgot Password ?"),
),
Expanded(
child: Container(),
),
FlatButton(
child: Text("Login"),
color: Color(0xFF4B9DFE),
textColor: Colors.white,
padding: EdgeInsets.only(
left: 38, right: 38, top: 15, bottom: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5)),
onPressed: () {},
)
],
)
],
),
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 40,
),
Text(
"Don't have an account ?",
style: TextStyle(color: Colors.grey),
),
FlatButton(
onPressed: () {
setState(() {
_authMode = AuthMode.SINGUP;
});
},
textColor: Colors.black87,
child: Text("Create Account"),
)
],
)
],
);
}
Widget upperHalf(BuildContext context) {
return Container(
height: screenHeight / 2,
child: Image.asset(
'assets/house.jpg',
fit: BoxFit.cover,
),
);
}
Widget lowerHalf(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
height: screenHeight / 2,
color: Color(0xFFECF0F3),
),
);
}
}
This is the plugin I found so far:
carousel_slider: ^1.3.1. But I think the purpose is different.
I know how to display an image but I couldn't manage to use the plugin above instead of the image.
You can Stack your Login Form with carousel_slider
and Positioned your Login Form with Positioned widget
You can see demo picture for effect
full code
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
final List<String> imgList = [
'https://images.unsplash.com/photo-1520342868574-5fa3804e551c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=6ff92caffcdd63681a35134a6770ed3b&auto=format&fit=crop&w=1951&q=80',
'https://images.unsplash.com/photo-1522205408450-add114ad53fe?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=368f45b0888aeb0b7b08e3a1084d3ede&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1519125323398-675f0ddb6308?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=94a1e718d89ca60a6337a6008341ca50&auto=format&fit=crop&w=1950&q=80',
'https://images.unsplash.com/photo-1523205771623-e0faa4d2813d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=89719a0d55dd05e2deae4120227e6efc&auto=format&fit=crop&w=1953&q=80',
'https://images.unsplash.com/photo-1508704019882-f9cf40e475b4?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8c6e5e3aba713b17aa1fe71ab4f0ae5b&auto=format&fit=crop&w=1352&q=80',
'https://images.unsplash.com/photo-1519985176271-adb1088fa94c?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=a0c8d632e977f94e5d312d9893258f59&auto=format&fit=crop&w=1355&q=80'
];
void main() => runApp(CarouselDemo());
final Widget placeholder = Container(color: Colors.grey);
final List child = map<Widget>(
imgList,
(index, i) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Stack(children: <Widget>[
Image.network(i, fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color.fromARGB(200, 0, 0, 0), Color.fromARGB(0, 0, 0, 0)],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
child: Text(
'No. $index image',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
]),
),
);
},
).toList();
List<T> map<T>(List list, Function handler) {
List<T> result = [];
for (var i = 0; i < list.length; i++) {
result.add(handler(i, list[i]));
}
return result;
}
class CarouselWithIndicator extends StatefulWidget {
#override
_CarouselWithIndicatorState createState() => _CarouselWithIndicatorState();
}
class _CarouselWithIndicatorState extends State<CarouselWithIndicator> {
int _current = 0;
#override
Widget build(BuildContext context) {
return Column(children: [
CarouselSlider(
items: child,
autoPlay: true,
enlargeCenterPage: true,
aspectRatio: 2.0,
onPageChanged: (index) {
setState(() {
_current = index;
});
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: map<Widget>(
imgList,
(index, 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)),
);
},
),
),
]);
}
}
class CarouselDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
//Manually operated Carousel
final CarouselSlider manualCarouselDemo = CarouselSlider(
items: child,
autoPlay: false,
enlargeCenterPage: true,
viewportFraction: 0.9,
aspectRatio: 2.0,
);
//Auto playing carousel
final CarouselSlider autoPlayDemo = CarouselSlider(
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: true,
enlargeCenterPage: true,
items: imgList.map(
(url) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
),
),
);
},
).toList(),
);
//Button controlled carousel
Widget buttonDemo() {
final basicSlider = CarouselSlider(
items: child,
autoPlay: false,
enlargeCenterPage: true,
viewportFraction: 0.9,
aspectRatio: 2.0,
);
return Column(children: [
basicSlider,
Row(children: [
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: RaisedButton(
onPressed: () => basicSlider.previousPage(
duration: Duration(milliseconds: 300), curve: Curves.linear),
child: Text('prev slider'),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: RaisedButton(
onPressed: () => basicSlider.nextPage(
duration: Duration(milliseconds: 300), curve: Curves.linear),
child: Text('next slider'),
),
),
),
]),
]);
}
//Pages covers entire carousel
final CarouselSlider coverScreenExample = CarouselSlider(
viewportFraction: 1.0,
aspectRatio: 2.0,
autoPlay: false,
enlargeCenterPage: false,
items: map<Widget>(
imgList,
(index, i) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(image: NetworkImage(i), fit: BoxFit.cover),
),
);
},
),
);
//User input pauses carousels automatic playback
final CarouselSlider touchDetectionDemo = CarouselSlider(
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: true,
enlargeCenterPage: true,
pauseAutoPlayOnTouch: Duration(seconds: 3),
items: imgList.map(
(url) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
),
),
);
},
).toList(),
);
//Non-looping manual Carousel
final CarouselSlider nonLoopingCarousel = CarouselSlider(
items: child,
//scrollPhysics: BouncingScrollPhysics(),
enableInfiniteScroll: false,
autoPlay: false,
enlargeCenterPage: true,
viewportFraction: 0.9,
aspectRatio: 2.0,
);
//Vertical carousel
final CarouselSlider verticalScrollCarousel = CarouselSlider(
scrollDirection: Axis.vertical,
aspectRatio: 2.0,
autoPlay: true,
enlargeCenterPage: true,
viewportFraction: 0.9,
pauseAutoPlayOnTouch: Duration(seconds: 3),
items: imgList.map(
(url) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
),
),
);
},
).toList(),
);
//create full screen Carousel with context
CarouselSlider getFullScreenCarousel(BuildContext mediaContext) {
return CarouselSlider(
autoPlay: true,
viewportFraction: 1.0,
aspectRatio: MediaQuery.of(mediaContext).size.aspectRatio,
items: imgList.map(
(url) {
return Container(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
child: Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
),
),
);
},
).toList(),
);
}
return MaterialApp(
title: 'demo',
home: Scaffold(
appBar: AppBar(title: Text('Carousel slider demo')),
body: Stack(
children: <Widget>[
ListView(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Manuell Carousel'),
manualCarouselDemo,
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Auto Playing Carousel'),
autoPlayDemo,
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Button Controlled Carousel'),
buttonDemo(),
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Full Screen Carousel'),
coverScreenExample,
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Carousel With Indecator'),
CarouselWithIndicator(),
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Pause When Touched Carousel'),
touchDetectionDemo,
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('No infinity scroll carousel'),
nonLoopingCarousel,
])),
Padding(
padding: EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: [
Text('Vertical scroll carousel'),
verticalScrollCarousel,
])),
Padding(
padding: EdgeInsets.only(top: 15.0),
//Builder needed to provide mediaQuery context from material app
child: Builder(builder: (context) {
return Column(children: [
Text('Full screen carousel'),
getFullScreenCarousel(context),
]);
})),
],
),Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Card(
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Your Login Form",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."),
),
],
),
),
),],
),
));
}
}