Set background image height, width, position in Flutter - flutter

I have a background image in my SliverAppBar. I've tried the BoxFit.contain, BoxFit.fill...etc, but none of them work for what I'd like to do.
Here is what I can get:
But here's what I want:
I see there is BoxFit.values but I cannot find any documentation showing how to use this (if it's the right thing?)
Here's my code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:my_app/Theme.dart' as MyTheme;
import 'package:my_app/ui/rule_section_details/RuleRow.dart';
#override
class SliverHeaderTest extends StatelessWidget {
final DocumentSnapshot ruleGroup;
SliverHeaderTest(this.ruleGroup);
#override
Widget build(BuildContext context) {
return Material(
child: CustomScrollView(slivers: <Widget>[
SliverAppBar(
floating: true,
backgroundColor: Color(int.parse(ruleGroup['color'])),
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
// background: Image.asset('assets/img/circular-image.png',
// fit: BoxFit.contain),
background: new Image(
image: new AssetImage(ruleGroup['image']),
height: MyTheme.Dimens.ruleGroupListIconHeight,
width: MyTheme.Dimens.ruleGroupListIconWidth,
),
title: Text(ruleGroup['name'],
style: MyTheme.TextStyles.ruleSectionPageTitle),
centerTitle: true,
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.share),
tooltip: 'Share',
onPressed: () {/* ... */},
),
],
),
StreamBuilder(
stream: Firestore.instance
.collection('rules')
.where("section", isEqualTo: ruleGroup['id'])
.orderBy("subsection")
.orderBy("subsubsection")
.orderBy("subsubsubsection")
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SliverList(
delegate: SliverChildListDelegate(
[
Container(
child: new Center(child: new Text('Loading...')),
)
],
),
);
}
return SliverPadding(
padding: EdgeInsets.only(top: 16.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return new RuleRow(snapshot.data.documents[index]);
}, childCount: snapshot.data.documents.length)));
})
]),
);
}
}

It is the Desired behavior of background: property of FlexibleSpaceBar - its Suppose to fill all the background area of the Appbar, now title here is not separate element to render below background, but a foreground widget of the FlexibleSpaceBar to show on top of background:
If You really need to separate the title & Image here you can't use background & title property, but Instead you need to use Column or ListView instead of FlexibleSpaceBar.
You can try the Following Code with Possible options:
Recommended Solution:
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 200.0,
floating: true,
// pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Row(
children: <Widget>[
Spacer(),
CircleAvatar(
radius: 54.0,
backgroundImage: NetworkImage(
"https://placeimg.com/640/480/animals",
),
),
Spacer(),
],
)),
),
This Image is with radius: 68.0,.
Following are using fixed Margins, might cause issue in responsive design, but still works.
With ClipOval:
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 200.0,
floating: true,
// pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Container(
margin:
EdgeInsets.symmetric(horizontal: 125.0, vertical: 50.0),
child: ClipOval(
child: Image.network(
"https://placeimg.com/640/480/animals",
),
),
)),
),
with CircleAvatar
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 200.0,
floating: true,
// pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Container(
margin:
EdgeInsets.symmetric(horizontal: 125.0, vertical: 50.0),
child: CircleAvatar(
radius: 30.0,
backgroundImage: NetworkImage(
"https://placeimg.com/640/480/animals",
),
),
)),
),
Update:
with ListView Option.
Note: AppBar height is Determined by expandedHeight: property & will not increase in case of increase in Image Radius.
SliverAppBar(
backgroundColor: Colors.blue,
expandedHeight: 200.0,
floating: true,
// pinned: true,
flexibleSpace: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
Row(
children: <Widget>[
Spacer(),
CircleAvatar(
radius: 68.0,
backgroundImage: NetworkImage(
"https://placeimg.com/640/480/animals",
),
),
Spacer(),
],
),
Center(
child: Text("Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
)),
),
],
),
),
),

Related

Shrink SliverAppBar Image animation Like Title Text in Flutter

I am trying to replicate the text shrink effect that comes naturally with a SliverAppBar with an image such that the background image of the sliverhead shrinks to a leading icon in the appbar of the sliverAppBar.
I have tried using the AnimatedPostioned flutter widget along with a scroll controller that toggles the state of the isSkrink boolean when the page is scrolled.
Below is AnimatedPosition widget:
AnimatedPositioned(
width: isShrink ? 10.0 : 200.0,
height: isShrink ? 10.0 : 200.0,
duration: Duration(seconds: 2),
curve: Curves.linear,
child: Stack(
children: [
Container(
child: Image.asset(
'assets/images/user-avatar.png'),
),
],
),
),
Below is the SliverAppBar with the AnimatedPositioned in the stack and the user icon in the leading param of the SliverAppBar
SliverAppBar(
leading: isShrink
? Padding(
padding: EdgeInsets.all(10),
child: CircleAvatar(
backgroundImage:
AssetImage('assets/images/user-avatar.png'),
backgroundColor: Colors.white,
radius: 3,
),
)
: Container(),
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Row(
children: <Widget>[
Text(
"Username",
style: bodyText1Style(
context,
color: isShrink ? Colors.black : Colors.white,
fontSize: isShrink ? 18 : 15,
fontWeight: FontWeight.w600,
),
),
],
),
background: Stack(
children: <Widget>[
AnimatedPositioned(
width: isShrink ? 10.0 : 200.0,
height: isShrink ? 10.0 : 200.0,
duration: Duration(seconds: 2),
curve: Curves.linear,
child: Stack(
children: [
Container(
child: Image.asset(
'assets/images/user-avatar.png'),
),
],
),
),
],
),
),
expandedHeight: size.height * 0.35,
);
This is the result of the code above:
After a long research I was able to come with this using https://stackoverflow.com/a/59323713/3636615 as reference:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
TransitionAppBar(
// extent: 250,
extent: 300,
avatar: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/user-avatar.png'), // NetworkImage(user.imageUrl),
fit: BoxFit.cover)),
),
title: "Emmanuel Olu-Flourish",
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Container(
child: ListTile(
title: Text("${index}a"),
));
}, childCount: 25))
],
),
);
Check out the TransitionAppBar implementation here:
https://gist.github.com/Oluflourish/2f0789bd2e8ee576a2d6364d709c1c14
Below is demo of the result:

Flutter - Widgets not scrolling in NestedScrollView

I am trying to make a page that contains a SliverAppBar, some Widgets with texts, and a List.
I used a NestedScrollView that contains the Sliver header, and a Column, that itself contains the widgets with texts and the list.
Here is the build function :
#override Widget build(BuildContext context) {
var _bar = widget.bar ;
_screenWidth = MediaQuery.of(context).size.width ;
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(_bar.name,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Image.network(
"https://s1.qwant.com/thumbr/0x380/1/2/2a39f1f558f2cbbec11a08e43493bde861d612add6be643dbc5ad6fe0b16fc/BDBE5B56-B1B4-586D-27C8A70A4A54E50A.jpg?u=https%3A%2F%2Fimages.bwwstatic.com%2Fcolumnpic6%2FBDBE5B56-B1B4-586D-27C8A70A4A54E50A.jpg&q=0&b=1&p=0&a=1",
fit: BoxFit.cover,
)),
),
];
},
body: _offer(_bar),
),
);
}
The _offer function is the main issue, here is it :
Widget _offer(Bar bar) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
width: _screenWidth / 4,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.fill,
image: Image.network("https://s1.qwant.com/thumbr/0x380/3/9/60c4de7be57ee1b7d24d07dde941c3027588bc313699cba9ef9ef8fb6c7fda/1280px-Hard_Rock_Cafe_Logo.svg.png?u=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F2%2F2c%2FHard_Rock_Cafe_Logo.svg%2F1280px-Hard_Rock_Cafe_Logo.svg.png&q=0&b=1&p=0&a=1").image
)
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
bar.name.toUpperCase(),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
"Bar category" + " - " + "Bar address",
style: TextStyle(
fontWeight: FontWeight.bold
),
),
Text(
"Opening hours" + " - " + "01.12.12.12.12",
style: TextStyle(
fontWeight: FontWeight.bold
),
),
Text(
"Happy Hour de 20h a 23h",
style: TextStyle(
color: Colors.deepOrange,
fontWeight: FontWeight.bold,
),
)
],
),
],
),
Image.asset("assets/favorites.png"),
Padding(padding: EdgeInsets.all(20),),
Center(
child: Text(
"Menu".toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25
),
),
),
Padding(padding: EdgeInsets.all(20)),
Flexible(
fit: FlexFit.loose,
child: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return productItem(context, _productsList[index]);
},
childCount: _productsList.length,
),
),
],
),
)
],
);
}
Here is the result :
Link to the gif : https://ibb.co/x8j6vvm
(if someone know how to integrate gif in a question, feel free to edit it !)
I want the data from second "TestName1" to the heart image to completely scroll with the rest of the page. How can I achieve this ?
Using a CustomScrollView removes the problem of adding a NestedScrollView. You can add the SilverAppBar to the CustomScrollView to implement this.
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(_bar.name,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)),
background: Image.network(
"https://s1.qwant.com/thumbr/0x380/1/2/2a39f1f558f2cbbec11a08e43493bde861d612add6be643dbc5ad6fe0b16fc/BDBE5B56-B1B4-586D-27C8A70A4A54E50A.jpg?u=https%3A%2F%2Fimages.bwwstatic.com%2Fcolumnpic6%2FBDBE5B56-B1B4-586D-27C8A70A4A54E50A.jpg&q=0&b=1&p=0&a=1",
fit: BoxFit.cover,
)),
),
SliverFixedExtentList(
itemExtent: 150.0,
delegate: SliverChildListDelegate(
[
// Your Can Add you body items here.
],
),
),
],
),
Hope this helps.

Flutter: How to enlarge an Image when scrolling to the top of a page in an app?

I have a page with an Image on the top and some other widget underneath it. The entire page is scrollable.
Does anyone know how to enlarge the image when scrolling to the top to create an effect similar to the one in Apple Music?
Here you find an example of what I'm trying to create:
https://i.stack.imgur.com/MiYLZ.gif
try to use Sliver
final bannerHigh = 150.0;
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xFF0084C9),
leading: IconButton(
icon: Icon(
Icons.blur_on,
color: Colors.white70,
),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
expandedHeight: bannerHigh,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text("Your title",
style: TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.w600)),
background: Image.network(
'image url',
fit: BoxFit.cover,
),
),
),
SliverList(
delegate: SliverChildListDelegate(
<Widget>[
],
),
),
],
),
);

How to move title on Appbar when scrolling

I wanna make flutter app.
How to make a moving title on appbar when scrolloing??
I attached app's status on GIF file.
Where do I need to set moving the title text?
I just make app like Uber app
#override
Widget build(BuildContext context) {
double sliderValue = 0.0;
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
child: SliverAppBar(
forceElevated: innerBoxIsScrolled ,
backgroundColor: Colors.transparent,
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.arrow_back),
color: Colors.black,
),
elevation: 0.0,
expandedHeight: 100.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.only(left: 15.0),
collapseMode: CollapseMode.none,
title: Text("Notification",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
)),
),
),
),
];
},
body: Center(
child: Text("Sample Text"),
),
),
);
}
Have to use CustomScrollView widget
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text('Title'),
pinned: true,
expandedHeight: 48,
),
SliverFillRemaining(
child: Center(
child: Text('sliver'),
)),
],
);

flutter - How can I make my appbar act as material search widget while being flexible app bar

I want my appbar to act as a fixed appbar when it's scrolled down, or user's searching something.
SliverAppBar(
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
),
But I want to draw it as a flexible appbar when it's scrolled up and user's not searching.
flexibleSpace: new FlexibleSpaceBar(
centerTitle: false,
title: new TextField(
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
hintText: '검색',
),
),
background: Stack(
fit: StackFit.expand,
children: <Widget>[
SizedBox(
height: 256.0,
child: Container(
child: Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Column(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(
size: 64.0,
),
),
Padding(padding: const EdgeInsets.only(bottom: 24.0)),
ListTile(
title: Text('Some Text'),
),
],
),
),
),
),
// This gradient ensures that the toolbar icons are distinct
// against the background image.
],
),
),
Search field is transformed to top-right little bit when scrolled up with second approach.
The effect can be achieved by moving title content to another SliverList.
Remove flexibleSpace from SliverAppBar, and move contents of flexibleSpace.background to SliverList before the SliverAppBar.
Example:
#override
Widget build(BuildContext context) {
return new CustomScrollView(
slivers: <Widget>[
new SliverList(
delegate: new SliverChildListDelegate(<Widget>[
Align(
alignment: Alignment.topLeft,
child: FlutterLogo(size: 64.0),
),
ListTile(
title: Text('Some Text'),
),
ListTile(),
])),
new SliverAppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0.0,
automaticallyImplyLeading: false,
pinned: true,
floating: false,
title: new TextField(
focusNode: _searchFocusNode,
style: Theme.of(context).primaryTextTheme.title,
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
suffixIcon: Icon(Icons.search),
hintText: '검색',
),
),
),
new SliverList(
delegate: new SliverChildListDelegate(List.generate(
100,
(i) => ListTile(
title: Text('Scroll'),
)).toList()),
),
],
);
}
I've written a getMaterialSerach(); method in this gist which has the exact material search view you need. just add getMaterialSearch() from this in your appBar: widget like below.
here is the gist for getMaterialSearch();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: getMaterialSearchBar(),
body: Center(
child: Container(),
),
);
}
Here's my code with a TextField and Tabs in a SliverAppBar:
NestedScrollView(
controller: model.mainScrollController,
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
/// https://github.com/flutter/flutter/issues/54059
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
automaticallyImplyLeading: false,
pinned: true,
floating: true,
snap: true,
expandedHeight: 100,
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: [
BackButton(
color: Colors.white,
),
Flexible(
child: TextField(
controller: model.searchController,
decoration: InputDecoration(
focusColor: Colors.blueAccent,
hoverColor: Colors.blueAccent,
fillColor: Colors.white,
filled: true,
isDense: true,
prefixIconConstraints: BoxConstraints(maxHeight: 24, maxWidth: 48),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(Icons.search_outlined),
),
hintText: 'Search...'),
),
),
IconButton(
icon: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
ExtendedNavigator.named('topNav').push(Routes.newExerciseView);
},
tooltip: 'Create Exercise',
),
],
),
),
),
bottom: TabBar(
labelPadding: EdgeInsets.only(bottom: 8),
indicatorWeight: 3,
indicatorSize: TabBarIndicatorSize.label,
tabs: [
Text('All'),
Text('Custom'),
Text('Favorites'),
],
),
),
),
];
},
body: TabBarView(
children: [
AllExercises(),
CustomExercises(),
FavoriteExercises(),
]),
),
There's a few key pieces that make this work right, the first being that you need to use the SliverAppBar's flexibleSpace property to add your search widget. If you try adding it in the title property, the TextField just get's squished but never goes off screen.
Second, make sure that the collapseMode of the FlexibleSpaceBar is set to CollapseMode.pin. This makes it so that the contents of the flexibleSpace scroll off screen and don't change size.
And finally, set pinned on the sliverAppBar to true so the TabBar sticks.