Flutter - can I make a SingleChildScrollView fill the available space? - android

I have a Flutter app which I'm building for Android. The structure goes broadly like this:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("")),
body: SingleChildScrollView(
child: Container(
decoration: const BoxDecoration(
gradient: ...
),
child: ...
),
)
);
}
The goal here is to have the gradient background fill all the screen below the app bar, and if the content is larger than that space then to make it scrollable.
If I omit the SingleChildScrollView, the Container fills the space. But of course if it overflows then there is no scrolling. With the code as above, the scroll view does its thing on small screens but on large screens the gradient background doesn't fill the whole available area.
If I change it around like this:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("")),
body: Container(
decoration: const BoxDecoration(
gradient: ...
),
child: Column(children: [
SingleChildScrollView(
child: ...
),
Expanded(child:Container())
]),
)
);
}
then the gradient fills the background but the scroll view doesn't do the right thing - the content overflows the screen but can't be scrolled. How do I get it to do both?

The reason you're facing this issue is that your container is inside the SingleChildScrollView Widget and is getting scrolled along the SingleChildScrollView Widget. And Removing the SingleChildScrollView will result in renderflex overflow error
The Expanded keyword will throw incorrect use of ParentDataWidget
Also, you have added SingleChildScrollView widget inside the column you have to swap these well
Here is an example that I have given which will help you achieve it.
Widget build(BuildContext context) {
return Scaffold(
body: Container(
// Add The Container gradient here
width: double.infinity,
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: Column(
children: [
Container(
height: 100,
width: 50,
color: Colors.red,
),
Container(
height: 100,
width: 50,
color: Colors.blue,
),
Container(
height: 1000,
width: 50,
color: Colors.yellow,
),
Container(
height: 1000,
width: 50,
color: Colors.orange,
),
],
)),
));
}

Related

How to set a background color for a widget inside a main widget in Flutter?

I have a Container widget in which all my codes are in for the particular page. It has a background image and inside the container, there is a Center widget. I want to set the background color of the center widget to white but while doing so, the entire screen's background is changing to white. How can I achieve that please?
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('Assets/images/loginbg2.jpg'),
fit: BoxFit.cover
),
),
child: Scaffold(
backgroundColor: Colors.transparent, //code for background image
body: Center(
child:Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.fromLTRB(40, 0, 0, 0),
child: Text(
"Welcome back!",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
)
),
SizedBox(
height: 20.0,
), //code for center widget
This is probably because the Column widget inside your Center widget is taking up the entire screen.
A Column widget (unless constrained) is taking up all the space it can get.
Try solving your issue by removing the Column widget for now and only use the container inside the Center widget and define it's height and width. See if the background you want is coming back in view.

Please explain like I'm 5: What is the purpose of MainAxisSize in Flutter?

I'm quite seeing the effect of MainAxisSize but I don't understand its purpose. Please explain like I'm 5. Thank you.
For further reference:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color(0xffEDE7F6),
body: SafeArea(
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
Container(
height: 100,
width: 100,
color: Color(0xff9575CD),
child: Text('Container 1'),
),
Container(
height: 100,
width: 100,
color: Colors.black45,
child: Text('Container 2'),
),
Container(
height: 100,
width: 100,
color: Colors.black12,
child: Text('Container 3'),
),
]),
),
),
);
}
}
Output on emulator:
Let's say you have a container of height 400 wrapped around a column containing 3 rows of height 100 (either defined by height or intrinsic by what is in there), that leaves you 100pt of height unused.
MainAxisSize.max says, distribute all space between the column's items, so create spaces of 50 between the rows.
MainAxisSize.min says, squeeze the rows together and leave unused space (100) at the end (or beginning or both ends depending on alignment)

Hero Animation intially overflows then adjusts

Building a hero animation for list->detail, with responsive layouts for screen sizes.
Initially upon transitioning I get a brief renderflex error then it adjusts and is fine.
I solved this with the column (phone) layout with a ListView but the other layout is a row (side by side screens). No matter what combination of flexes and expanded and Lists and every other widget I could think of, I can't get it solved. Thanks
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Hero(
tag: content.timestamp,
child: MediaQuery.of(context).size.width < 600
? ... Column layout
: Row(
children: [
FadeInImage.assetNetwork(
placeholder: 'assets/icons/barc-lodge.png',
image: 'https://www.barclodge.ca/android/diary/${content.image}',
width: 300,
),
// Expanded exists to wrap text
Expanded(
child: diaryContent(context, content),
),
],
),
),
onTap: () => Navigator.pop(context),
),
);
}
Widget diaryContent(BuildContext ctx, BarcLodgeDiary words) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(12.0, 12.0, 0.0, 0.0),
child: Text(words.title, style: Theme.of(ctx).textTheme.headline1),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text('${words.datestamp} at ${words.timestamp}', style: Theme.of(ctx).textTheme.caption),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Text(words.diaryentry, style: Theme.of(ctx).textTheme.bodyText1),
),
],
);
}
This might be your issue. The Hero widget needs a Material widget inside it to provide a canvas for the splash, you can read about it in the docs here. The section is about building your own hero widget but I think it still applies, if this doesn't fix your issue, try wrapping the Hero widget with the Material widget.
One thing I did not try apparently in a couple hours was GridView. Remove the Expanded, change Row to GridView.count and everything is a-ok.

Is it possible to fill ExpansionTile title with image in Flutter?

I want to create an expandable gallery, which has one image as a main image and upon clicking on it, I want to expand the gallery and show few images below the main image.
So, I have created a ListView, which has a builder that returns ListTileTheme, which I used for removing the contentPadding, with child - ExpansionTile, but I still have some padding left in the container.
Any idea how to completely fill the ExpansionTile? I've tried different approaches that I saw on the web, but no solution so far.
Here is my code:
Container _getGallery() {
Container container = new Container(
height: 500.0,
child: new ListView.builder(itemCount: 1, itemBuilder: (context, i)
{
ListTileTheme tileTheme = new ListTileTheme(
contentPadding: EdgeInsets.all(0.0),
child: ExpansionTile(
trailing: Container(
height: 0.0,
width: 0.0,
),
backgroundColor: Colors.transparent,
title: Image.asset("assets/images/onboarding2.png",
height: 200.0,
width: double.infinity,
alignment: Alignment.center,
fit: BoxFit.cover,
),
children: <Widget>[
new Column(
children: _buildExpandableContent(),
),
],
),
);
return tileTheme;
}),
);
return container;
}
Here you can see what I've achieved so far (1- not expanded, 2- expanded). I want to remove this white lines around the first image.
No, it's not possible. This widget uses ListTile, which adds 16px gap (_horizontalTitleGap constant) if there's trailing widget. The problem is that there's always a trailing widget in ExpansionTile, even if you pass null:
trailing: widget.trailing ?? RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
The only dirty workaround I can suggest is to use scale transform to compensate this 16px gap:
title: LayoutBuilder(
builder: (context, constraints) {
final newScale = 1.0 + 16.0 / constraints.maxWidth;
return Container(
transform: Matrix4.identity()..scale(newScale),
child: Image.asset("assets/images/onboarding2.png",
height: 200.0,
width: double.infinity,
alignment: Alignment.center,
fit: BoxFit.cover,
),
);
}
),

Borderless selection highlight effect in Flutter

Context: I'm rewriting my Android app with Flutter. On Android, there was this interesting touch feedback effect when setting clickable View's background property to ?android:selectableItemBackgroundBorderless:
Note that the red border wasn't in the real UI, it's there just to show View's borders. As you can see, the ink forms a circle that is circumscribed around the rectangular view.
I would like the ink in my Flutter app to be also circumscribed around the view, i.e. the selection area needs to be circular and to encompass the view. I'm trying to achieve this by using InkResponse component, but the result looks miserable:
The body of the Scaffold used in the example:
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Material(
color: Color(0xff008080),
child: Center(
child: InkResponse(
onTap: () {
/* ... */
},
child: Container(
child: Center(
child: Text('BUTTON'),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
width: 200.0,
height: 200.0,
),
highlightColor: Colors.transparent,
),
),
),
),
If I make the radius property of the InkResponse big enough, it can go beyond the view's bounds, and if my view had static size, I could tweak the property to achieve the desired effect, but in my real app it's dynamic.
Is it possible to do it without making a custom component?
For these purposes, I think InkResponse should be used:
Container(
width: 80,
height: 80,
child: InkResponse(radius: 110, onTap: () {}, child: Text("text")));
Working Example
Have you tried this, its click is bound into the red area.
Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Material(
color: Color(0xff008080),
child: Center(
child: InkWell(
onTap: () {
/* ... */
},
child: Container(
child: Center(
child: Text('BUTTON'),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
),
highlightColor: Colors.transparent,
),
),
),
),

Categories

Resources