Related
I'm trying to make e-commerce app in flutter.
I wanted to make Appbar transparent and have animation, so I use Sliverappbar but I can't make it transparent without scrolling down.
I tried to use stack, but it doesn't work and has error.
I want appbar transparent when it's on top and change white when I scroll down.
This is my flutter code
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
leading: Icon(
Icons.menu,
size: 30,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.tune, color: Colors.black, size: 30),
)
],
),
body: _buildBody(),
);
}
Widget _buildBody() {
return CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: Icon(
Icons.menu,
size: 30,
),
backgroundColor: Colors.transparent,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.tune,
color: Colors.black,
size: 30,
),
)
],
floating: true,
elevation: 0.0,
snap: false,
),
SliverToBoxAdapter(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
child: Carousel(
images: [
Image.network(
'https://i.pinimg.com/564x/83/32/37/8332374f18162612dd9f2a4af2fda794.jpg',
fit: BoxFit.cover),
Image.network(
'https://i.pinimg.com/originals/e2/8e/50/e28e5090b7193ec9b2d5b5c6dfaf501c.jpg',
fit: BoxFit.cover),
Image.network(
'https://image-cdn.hypb.st/https%3A%2F%2Fhypebeast.com%2Fwp-content%2Fblogs.dir%2F6%2Ffiles%2F2019%2F09%2Fmschf-fall-winter-lookbook-streetwear-seoul-korea-47.jpg?q=75&w=800&cbr=1&fit=max',
fit: BoxFit.cover)
],
showIndicator: false,
)),
),
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(
left: 25.0, top: 20.0, right: 0.0, bottom: 20.0),
child: Text('Recommended for You',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25)),
),
),
SliverPadding(
padding: EdgeInsets.only(left: 35.0, right: 35.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 20.0,
crossAxisSpacing: 25.0,
childAspectRatio: 0.67,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return _buildListItem(context, index);
},
childCount: 13,
),
))
],
);
Best solution I can find is, instead of using SliverAppBar, use a regular AppBar in a SliverToBoxAdapter. You would set the flexibleSpace property to your Carousel, or put the AppBar and carousel into a Stack.
The flexibleSpace property, as far as I can tell, behaves differently in a SliverAppBar and a regular AppBar. It wont collapse in a regular AppBar and you also won't need to put your carousel in a FlexibleSpaceBar().
You may need to do a few additional things to get the exact look you're going for (e.g. change the elevation).
You can simply warp SliverAppBar with SliverOpacity widget
SliverOpacity (
opacity: 0.5,
sliver: SliverAppBar(
leading: Icon(
Icons.menu,
size: 30, ),
backgroundColor: Colors.transparent,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.tune,
color: Colors.black,
size: 30,
),
)
],
floating: true,
elevation: 0.0,
snap: false,
),
)
Stack has 3 child properties, first child (red background) is partially visible because last child (black background) is on top. I need the last child to be directly below the other two and it should not overlap as it is doing now. Last child contains dynamic text - more than one line of text causes the text block to shift up instead of down - trying adding a few lines to the last child.
return new Scaffold(
body: NestedScrollView(
controller:_scrollController ,
headerSliverBuilder:
(BuildContext contrxt, bool innerBoxIsScrolled) {
print('scrolled $innerBoxIsScrolled');
return <Widget>[
SliverAppBar(
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
expandedHeight: 195.0,
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
flexibleSpace: FlexibleSpaceBar(
background: new Stack(children: <Widget>[
Positioned(
right: 0.0,
left: 0,
bottom: -1,
child: Container(
color: Colors.redAccent,
height: 50.0,
child: Text("Container 1 Text", textAlign: TextAlign.center,),
),
),
Container(
color: Colors.blue,
height: MediaQuery.of(context).size.height / 6,
width: MediaQuery.of(context).size.width,
child: Text("Container 2 Text", textAlign: TextAlign.center,),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
//margin: EdgeInsets.only(top: 49.0),
color: Colors.black,
//height: 20.0,
width: MediaQuery.of(context).size.width,
//padding: EdgeInsets.only(top: 5.0, left: 20.0, bottom: 5.0, right: 20.0),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: new Text("Container 3",
style: TextStyle(fontSize: 12.0, color: Colors.grey[800]),
),
),
),
)
]),
),
),
];
},
body: Container(),
Well, I am going to give an answer for the "expanding upward" problem. At the Positioned widget which holds the "Container 3 Text", you have set the parameters like this;
Positioned(
bottom: 0,
left: 0,
right: 0,
//codes continue.. )
The problem here is, when you set the bottom property as "0", then it is going to be a fixed position for this widget in the bottom side and when this widget is expanding, it will not change the fixed bottom position and that is why it is going to expand to upward. So, instead of this, use top property to position this widget vertically. Once you set top property, then the top position of this widget will be fixed and you are going to see that it is expanding downward. For example;
Positioned(
top: 130,
left: 0,
right: 0,
//codes continue.. )
Addition: You must be considering that, even thought your widget is going to expand downward after this, it will never cross the borders of its parent widget. The parent widget SliverAppBar has the expandedHeight property, and you set is as "195.0". Anything goes beyond of this height scope, will not be displayed. From the documentation about expandedHeight property;
If a [flexibleSpace] widget is specified this height should be big
enough to accommodate whatever that widget contains.
Since your Text widgets are flexible, you must be setting enough space for the expandedHeight property.
I'm trying to get a Material Card with custom shadow (that why I don't use the Card's elevation), It work fine when the color of the child controller is set to white, but the shadow disappear (become much weaker) when the child container is filled with darker color like brown.
This is the code
Container(
padding: EdgeInsets.only(
top: 16.0,
left: 16.0,
right: 16.0,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.10),
blurRadius: 8.0,
),
],
),
child: Card(
clipBehavior: Clip.antiAlias,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
child: Container(
/// If this Container is filled then the shadow is gone
color: Colors.brown,
),
),
),
);
I even tried putting a CropRRect over the Card or container, but does not work. Can anyone explain to be why this is the case or anything I can do to keep the shadow the same?
It does not disappear, it's just a visual effect, you can try with:
BoxShadow(
color: Colors.black.withOpacity(0.50),
blurRadius: 18.0,
),
and
color: Colors.brown,
The margin between leading and title is too much;
How to decrease it;
I have tried several ways:
warp the leading with container and set margin right negative;
warp the title and set padding-left
however, it does not work at all;
is there any solution, i do need help
you're ultimately better off building your own containers - there's nothing special or complicated about ListTile. that way you can easily customize things like the spacing between a title and a button. just use something like so:
Container(
padding: new EdgeInsets.symmetric(vertical: 6.0, horizontal: 6.0),
margin: EdgeInsets.symmetric(vertical: 6.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: Colors.black),
),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
icon: Icon(myLeadingIcon),
onPressed: () => {},
),
Padding(padding: EdgeInsets.only(left: 20.0)),
Text(_myTitle),
],
),
...
The only answer that worked for me is to Matrix transform the title widget.
Here, the title text padding is decreased by 16.
ListTile(
leading: Icon(icon),
title: Transform(
transform: Matrix4.translationValues(-16, 0.0, 0.0),
child: Text("Title text",
style: TextStyle(fontSize: 18, color: textPrimary)),
),
);
Source: How to set the padding between leading and title from Flutter ListTile?
UPDATE
Now you can also use the following propertier:
horizontalTitleGap - Between title and leading
minVerticalPadding - Between title and subtitle
minLeadingWidth - Minimum width of leading
contentPadding - Internal padding
OLD
You can use the visualDensity property to reduce the space.
ListTile(
visualDensity: VisualDensity(horizontal: -4, vertical: 0),
title: Text("xyz")
);
The visualDensity value can be changed from -4.0 to 4.0. Lower the value, more compact the view.
P.S. This solution is similar to a different question
This question is about the gap between leading and title. But the other question is about top/bottom spacing
Don't use leading just use Row inside title
title: Row(
children: <Widget>[
Icon(Icons.location_on,size: 15,color:ThemeManager.mainContentColor,),
SizedBox(width: 8,),
Text('DEMO'),
],
),
Inside ListTile, Use contentPadding : EdgeInsets.fromLTRB()
you can then tweak the R value to suit your design.
An example with a Container:
Container(
child: ListTile(
contentPadding: EdgeInsets.fromLTRB(10.0, 0.0, 250.0, 0.0),
leading: CircleAvatar(
backgroundColor: Colors.purpleAccent,
child: Text('A'),
),
trailing: Text(
'Any Text here',
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
),
),
),
),
Set minLeadingWidth to 0.
ListTile(
minLeadingWidth: 0, // <-- Set this.
leading: Icon(Icons.settings),
title: Text('Settings'),
)
I think it's quite late to reply to this, but I think this can help others. I faced similar kind of problem. The below code helped me to solve this:
Card(
child: ListTile(
leading: Icon(
Icons.call,
color: Colors.teal,
size: 20
),
title: Align(
child: Text(
"xxxxxxxxxx"
),
alignment: Alignment(-1.3, 0),
),
dense: true,
)
So, basically combination of Alignment property to title and dense: true eventually helped me to solve this.
Note: Use alignment value according to your use case.
I hope this will help you!
You can set horizontalTileGap
ListTile( horizontalTitleGap: 0, //Set this. minLeadingWidth: 0, leading: Icon(Icons.settings), title: Text('Settings'),)
I have the same problem these days, and finally I published one package which may can solve this problem.
The package is available at https://pub.dev/packages/list_tile_more_customizable, and with this package we can set the horizontalTitleGap, and when it has been set to 0.0(zero), the padding between leading and title will become zero.
Usage:
ListTileMoreCustomizable(
title: Text("Title"),
trailing: Icon(Icons.people),
horizontalTitleGap: 0.0, // This horizontalTitleGap can set the margin between 'leading' and 'title' and also between 'trailing' and 'title'.
onTap: (details){},
onLongPress: (details){},
);
Edited:
This method works great for me, and also the code is available at https://github.com/Playhi/list_tile_more_customizable (the raw code is too long), I'm hard to understand why one user down-voted the answer without submitting any problems on it.
Set minLeadingWidth: 0 to remove the gap between leading and title and set horizontalTitleGap: 7 to adjust your custom gap between leading and title.
ListTile(
minLeadingWidth: 0, // min width of leading; if 0 => leading won't take extra space
horizontalTitleGap: 7, //gap between title and leading
leading: SvgPicture.asset(icChecked),
title: const Text('Demo Text', style: tsWhiteSemiBold16),
),
If you wants your leading in CenterVertical , so you have to wrap the leading icon inside a Container
ListTile(
minLeadingWidth: 0, // min width of leading; if 0 => leading won't take extra space
horizontalTitleGap: 7, //gap between title and leading
leading: Container(
height: double.infinity,
child: SvgPicture.asset(icChecked),
),
title: const Text('Demo Text', style: tsWhiteSemiBold16),
),
You can also user contentPadding to adjust the padding of the ListTile children.
setting dense true may fix your problem.
ListTile(
dense: true
)
If have use Divider for line in ListTile or ListView.separated then set Divider height 0, because Divider take by default some height.
Divider(
height: 0,
);
This is working properly for me.
After Flutter 2.0 upgrade
ListTile has received a minLeadingWidth property.
Default value is 40, so to reduce space between leading and title by x pass minLeadingWidth: 40 - x.
Align results will depend on text and tile width.
Use Transform.translate for consistent results.
ListTile(
leading: Icon(icon),
title: Transform.translate(
offset: Offset(-16, 0),
child: Text('Some text'),
),
);
As of June 9 of 2021, you can implement that like this, using horizontalTitleGap:
ListTile(
horizontalTitleGap: 2,
title: Text("Title Number ${index + 1}")
)
I'm using a TabBar widget and I'd like to customize the height and width of the indicator. I can't see any other property besides color I can modify.
You can use indicatorSize: TabBarIndicatorSize.label on the TabBar to make the indicator the same size as the label.
Or you could set the indicator directly, this is a Decoration which you can customize:
AppBar(
bottom: TabBar(
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 5.0),
insets: EdgeInsets.symmetric(horizontal:16.0)
),
tabs: [
Tab(text: 'tab 1'),
Tab(text: 'tab 2'),
Tab(text: 'tab 3'),
],
),
);
For more customisation options check this post
Give your TabBar a property of isScrollable: true if you don't want the tabs to expand to fill the screen horizontally the way they do by default.
You can use a Container wrapped in a PreferredSize to size the TabBar. (The PreferredSize is only necessary if you want it to live in the bottom slot of an AppBar.) This has the effect of making the indicators appear narrower because the TabBar doesn't fill the screen. However, the indicator has a hard coded height. If you don't like it, you'll have to import your own copy of tabs.dart and customize the constants in that file.
Note that you can also use a Container to set the height of the individual Tabs, although that doesn't seem like what you're trying to do.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new DefaultTabController(
length: 2,
child: new Scaffold(
appBar: new AppBar(
title: new Text('Tabs Demo'),
bottom: new PreferredSize(
preferredSize: new Size(200.0, 200.0),
child: new Container(
width: 200.0,
child: new TabBar(
tabs: [
new Container(
height: 200.0,
child: new Tab(text: 'hello'),
),
new Container(
height: 200.0,
child: new Tab(text: 'world'),
),
],
),
),
),
),
// body: ...
),
);
}
}
In the same way as this answer https://stackoverflow.com/a/44273493/5938089, The best is to use containers, but I will only use one.
For a change, I will use a bottom bar
bottomNavigationBar: new Material(
color: Colors.teal,
child: new Container(
height: 60.0,
child: new TabBar(
controller: controller,
tabs: <Widget>[
new Tab(icon: new Icon(Icons.access_alarm)),
new Tab(icon: new Icon(Icons.account_balance)),
]
),
)
),
Screen Shot
You can adjust the spacing between the tabs -> labelPadding: EdgeInsets.symmetric (horizontal: 5),
You can use property TabBar(indicatorWeight:detail Height).
Check out Tab Indicator Styler package for more advanced styling.
appBar: new AppBar(
title: Text("TabBar demo"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: Align(
alignment: Alignment.centerLeft,
child: new TabBar(
controller: _tabController,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(width: 3.0, color: Colors.red),
insets: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10),
),
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
onTap: (index) {
print(index);
_currentIndex = index;
setState(() {});
},
tabs: [
new Container(
height: 50.0,
width: 80,
// color: Colors.red,
child: new Tab(text: '1'),
),
new Container(
height: 50.0,
width: 80,
// color: Colors.red,
child: new Tab(text: '222'),
),
new Container(
height: 50.0,
width: 80,
// color: Colors.red,
child: new Tab(text: '333333'),
),
],
),
),
),
),
// body: ...
);
Try to do this in the TabBar
indicatorWeight: 0,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
width: 0.1,
color: AppColor.themeColor,
),
You can design the underline indicator with border-radius, and the length of the indicator according to the text container length.
child: TabBar(
indicatoenter code herer: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: widgetBlueColor,
),
indicatorPadding: const EdgeInsets.only(top: 33, bottom: 2),
labelColor: widgetBlueColor,
unselectedLabelColor: Colors.black87,
labelPadding: const EdgeInsets.only(bottom: 16),
indicatorSize: TabBarIndicatorSize.label,
labelStyle: const TextStyle(fontWeight: FontWeight.w500),
tabs: const [
Tab(
child: SizedBox(
width: 60,
child: Text(
"DETAILS",
textAlign: TextAlign.center,
),
),
),
Tab(
child: SizedBox(
width: 85,
child: Text(
"DOCUMENTS",
textAlign: TextAlign.center,
),
)
)
]
)
if you decrease default tab bar size use SizeBox().
SizedBox(
height: 40,
child: TabBar(
isScrollable: true,
indicatorSize: TabBarIndicatorSize.tab,
labelColor: Colors.black,
unselectedLabelColor: Colors.grey,
labelStyle: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500),
unselectedLabelStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400),
padding:
EdgeInsets.symmetric(horizontal: 12),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: AppTheme().orange),
tabs: getTab(),
controller: _tabController),
),