i have the following simple code
#override
Widget build(BuildContext context) {
return
NestedScrollView(
headerSliverBuilder: (context,value){
return[
SliverAppBar(
// expandedHeight: ??,
backgroundColor: Colors.transparent,
flexibleSpace: FlexibleSpaceBar(
background: headWidget()
),
)
];
},
body: myWidget()
);
}
}
now how can i set the SliverAppBar expandedHeight to auto suit my headWidget() height
currently i use ScreenUtil plugin to set expandedHeight manually but is there any other auto way ?
any idea ?
Related
I have a fairly complicated screen I am trying to implement in Flutter.
It's a scrollview with a parallax background and...kind of a collapsing toolbar.
I know I have to probably use a NestedScrollView and SliverAppBar(?), but not sure where to start on implementing. I think a picture would best show what I am trying to accomplish:
The list starts below a Container. As you scroll the list, the Container shrinks to a smaller Container and is pinned to the top. Does that make sense? Any help would be greatly appreciated!
This is using SliverAppBar with expandedHeight. I will encourage checking this video.
#override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: constraints.maxHeight * .3,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text("Title"),
background: Container(
color: Colors.pink,
),
),
),
SliverToBoxAdapter(
child: Container(
height: constraints.maxHeight * 4,
color: Colors.deepOrange,
),
)
],
),
),
);
}
I am trying to put a text or container behind bottom Navigation Bar like pinterest bottom navigation
Container behind bottom navigation bar
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
backgroundColor: Colors.red,
body: SafeArea(
child: Container(
height: 1000,
width: 200,
color: Colors.blue,
),
),
Add this to your Scaffold:
extendBody: true,
I want to get the whole height of CustomScrollView widget. So I made a below code but it's not working.
#override
void initState(){
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => getSizeAndPosition());
}
getSizeAndPosition() {
RenderBox _customScrollBox =
_customScrollKey.currentContext.findRenderObject();
_customScrollSize = _customScrollBox.size;
_customScrollPosition = _customScrollBox.localToGlobal(Offset.zero);
print(_customScrollSize.height);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _customScrollKey,
appBar: _appbar(),
body: CustomScrollView(
controller: _controller,
slivers: [
SliverList(
delegate: SliverChildListDelegate([
_titleSection(),
_thumnail(),
SizedBox(
height: 40,
),
])),
],
),
);
}
The height obtained by this code does not take into account the height of the list in the customScrollview. I mean, _customScrollSize.height and MediaQuery.of(context).size.width are the same.
I want this function
_controller.addListener(() {
setState(() {
if (_controller.offset < 0) {
scrollHeight = 0;
} else {
scrollHeight = _controller.offset;
}
});
});
Container(
width: size.width * (scrollHeight / _customScrollSize.height),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1)),
With the above code, '_customScrollSize.height' does not reflect the overall height and therefore the function is not implemented properly. Is there any good way to use it in this situation?
CustomScrollView
A ScrollView that creates custom scroll effects using slivers.
A CustomScrollView lets you supply slivers directly to create various scrolling effects, such as lists, grids, and expanding headers. For example, to create a scroll view that contains an expanding app bar followed by a list and a grid, use a list of three slivers: SliverAppBar, SliverList, and SliverGrid.
In your case do remove the appBar and use a sliverAppBar withing the custome scrollview , and you can use sliverfillRemaining widget for your other children
example
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverList(
delegate: SliverChildListDelegate([
_titleSection(),
_thumnail(),
SizedBox(
height: 40,
),
])),
...
I'm trying to make an event page for an app where user can view events that have a banner image and some other useful information. I really like the idea of implementing a SliverAppBar with the banner, so that the user can scroll to see more information. For this I seem to need a CustomScrollView with a SliverAppBar and FlexibleSpaceBar.
All tutorials I have seen online assume that the rest of the screen should be a list of sorts, but I rather want something like a Column widget. A Column has unbounded height, however, which causes overflow errors in the CustomScrollView. I could wrap it in a Container with specified height, but the contents of the body are of variable size, so that is not ideal. Is there a way to have a SliverAppBar and a Column work side by side?
I want something along the lines of this:
class ActivityPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
flexibleSpace: FlexibleSpaceBar(
background: Image(someImage),
),
expandedHeight: Image,
floating: false,
pinned: true,
snap: false,
),
Column(
children: [
someChildren,
]
),
)
]),
),
);
}
It should be possible, because it seems to me a somewhat common pattern, but I have looked around a lot and I can only find examples where the body consists of lists...
For anyone having the same struggle: here's the solution I just found:
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: this.color,
flexibleSpace: FlexibleSpaceBar(
background: YourImage(),
),
)
];
},
body: Container(
child: Builder(builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
WidgetOne(),
WidgetTwo()
]);
})),
),
)),
);
}
Use SliverList and SliverChildListDelegate instead of a Column.
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Container(color: Colors.green),
),
),
SliverList(
delegate: SliverChildListDelegate([
Container(color: Colors.yellow, height: 400),
Container(color: Colors.red, height: 800),
]),
),
],
),
);
}
Use ListView instead of Column. ListView has dynamic size
For me the best way is to use SliverToBoxAdapter. Just wrap your Column in a Container and then wrap this Container in a SliverToBoxAdapter and it should work fine.
I’m working on the concept that you can see on the screenshot below:
design concept
Note: the arrows are not the part of the UI, but were added to demonstrate the draggable functionality.
The screen has a SliverAppBar that displays location title, Sliver body that contains location description, and has a DraggableScrollableSheet (or a similar alternative).
When the location description is scrolled up, the title collapses.
When the DraggableScrollableSheet is scrolled up it expands to the full height of the screen.
I tried many times to put it together, but something is always off.
My last attempt was to add DraggableScrollableSheet as a ‘bottom sheet:’ in Scaffold. Since I have a BottomAppBar, it breaks the UI, and looks the following way:
current UI behavior
Scaffold
#override
Widget build(BuildContext context) {
return Scaffold(
body: body,
extendBody: true,
appBar: appBar,
bottomSheet: hasBottomSheet
? DraggableScrollableSheet(
builder:
(BuildContext context, ScrollController scrollController) {
return Container(
color: Colors.blue[100],
child: ListView.builder(
controller: scrollController,
itemCount: 25,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
);
},
)
: null,
backgroundColor: Colors.white,
floatingActionButtonLocation: fab_position,
floatingActionButton: hasActionButton ? ScannerFAB() : null,
bottomNavigationBar: AppBarsNav(hasNavButtons: hasNavButtons));
}
Scaffold body
class LocationPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScaffoldWithNav(
hasBottomSheet: true,
body: CustomScrollView(
slivers: <Widget>[
SliverBar(
title: "Location",
hasBackground: true,
backgroundImagePath: 'assets/testImage.jpg'),
SliverToBoxAdapter(
child: Text("very long text "),
),
SliverPadding(
padding: EdgeInsets.only(bottom: 70),
),
],
),
);
}
}
BottomAppBar FAB
class ScannerFAB extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FloatingActionButton(
child: WebsafeSvg.asset('assets/qr-code.svg',
color: Colors.white, height: 24, width: 24),
);
}
}
The FAB jumps, the content is hidden.
When I set a fixed-sized container, the content comes back, but the FAB is still living its own life:)
current UI behavior2
If anyone has any idea how to solve this issue/those issues please share, I’ll be very grateful!
You can try to add another Scaffold on current body and put the DraggableScrollableSheet inside it. Then the DraggableScrollableSheet won't affect the FAB outside.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Scaffold(
body: body,
bottomSheet: ... // move DraggableScrollableSheet to here
),
...
floatingActionButton: ... // keep FAB here
...
)
You can use Stack into Body, for example:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children. [
SingleChildScrollView(),
DraggableScrollableSheet(),
]
),
...
floatingActionButton: ... // keep FAB here
...
)