How to scroll parent widget from child? - flutter

I have some stateless scrollable widget like this
`class ProfileScreen extends StatelesslWidget {
final dataKey = new GlobalKey();
#override
Widget build(BuildContext context) {
BlocProvider.of<StepsBloc>(context).add(CheckStatus());
return Scaffold(
backgroundColor: NorbuColors.darkGrey,
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(
left: 20, right: 20, top: 48, bottom: 15),
child: _ProfileReg(),
),
),
SliverToBoxAdapter(
key: dataKey,
child: _AchievementsStatistics(),
),
SliverToBoxAdapter(
child: _StepsStatistics(),
),
SliverToBoxAdapter(
child: _MyGoalsStatistics(),
),
SliverToBoxAdapter(
child: _GratefulTimerStatistics(),
),
SliverToBoxAdapter(
child: _WheelBalanceStatistics(),
),
],
),
);
}`
And my question is: what is a good way to scroll parent widget from child?
I know easy way is to use
Scrollable.ensureVisible(dataKey.currentContext!);
but it works only inside scrollable parent widget. Thanks!

Related

How to make whole screen scrollable in Flutter?

I have an issue with the whole screen scrolling in Flutter. Only the Expanded widget is able to scroll on-screen in my app but I want to make the whole screen scrollable. How can I do that? Here is my code e.g.
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
'My App',
style: AppTheme.of(context).title1,
),
elevation: 0,
backgroundColor: Colors.amber,
),
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(10, 20, 10, 0),
child: Column(
. // Padding repeats two more times
.
.
.
Expanded(
child: MyWidget(
myProp1: value1,
myProp2: value2,
myProp3: value3,
myProp4: value4,
myProp5: value5,
myProp6: value6
) // MyWidget
), // Expanded
],
), // Column
); // Scaffold
}
I've tried to wrap Column with SingleChildScrollview widget but it didn't work for me. I guess I can not use Expanded widget in SingleChildScrollview. I also tried to use Container or etc. instead of Expanded and Column widgets but I couldn't solve this issue. Can anybody help me with it?
There is no need to wrap MyWidget with Expanded. Just Change Column into ListView:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
'My App',
),
elevation: 0,
backgroundColor: Colors.amber,
),
body: ListView(
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(10, 20, 10, 0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 500,
decoration: BoxDecoration(color: Colors.black),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 500,
decoration: BoxDecoration(color: Colors.black),
),
)
],
))
],
), // Column
); // Scaffold
}
You also can use the SingleChildScroll view as Child of Expanded widget will work fine

How to add custom container to form

I'm new to flutter. I'm trying to build register and login app.
but I faced an issue. My problem is that I have this container
class Background extends StatelessWidget {
final Widget child;
const Background({
Key? key,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Positioned(
top: 0,
right: 0,
child: Image.asset(
"assets/images/img1.png",
width: size.width
),
),
Positioned(
top: 0,
right: 0,
child: Image.asset(
"assets/images/img2.png",
width: size.width
),
),
child
],
),
);
}
}
I need to include this container in Scaffold here to take the design
but I didn't know how can I do that.
return Scaffold(
body: Form(
key: formkey,
child: Column()));
Can Anyone help me to do that please
Background is a wrapper widget. Do it like
Scaffold(
body: Background(
child: Form(
child: Column(
children: [],
),
),
),
),

How to avoid other stack containers?

I have a chat widget that I've been having trouble with given the unique header. Finally figured out how to position the TextComposer at the base of the app and all looks great, however the ListView where the chat's occur overlaps the header after a certain number of text entries. I've been trying to figure out how to position the ListView.builder so that it stays below the header (_buildImage and _buildTopHeader) and doesn't overlap such as in the image below. Unfortunately I haven't been having any luck. Any suggestions would be greatly appreciated!
Thanks!
Header Overlap
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: Stack(children: <Widget>[
_buildIamge(),
_buildTopHeader(),
Container(
alignment: Alignment.center,
child: new ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
)),
]),
),
Container(
decoration:
new BoxDecoration(color: Theme.of(context).cardColor),
height: 50.0,
child: _buildTextComposer(),
alignment: Alignment.bottomCenter,
),
],
),
),
resizeToAvoidBottomPadding:false,
);
}
Stack renders its children list in order from top to bottom, with stuff on the bottom rendered over things at the top (it makes sense, I promise). So if you want the ListView to be rendered at the bottom of everything else, put it at the top of the children list:
Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
child: new ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
),
),
_buildIamge(),
_buildTopHeader(),
],
),
Take a try with Positioned
Demo:
import 'package:flutter/material.dart';
class StackPositionPage extends StatefulWidget {
StackPositionPage({Key key, this.title}) : super(key: key);
final String title;
#override
_StackPositionPageState createState() => _StackPositionPageState();
}
class _StackPositionPageState extends State<StackPositionPage> {
var _headerHeight = 100.0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: buildBody(context),
);
}
Widget buildBody(BuildContext context) {
return SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: Stack(children: <Widget>[
_buildIamge(),
_buildTopHeader(),
Positioned(
top: _headerHeight,
left: 0,
right: 0,
bottom: 0,
child: ListView.builder(
padding: new EdgeInsets.all(8.0),
itemBuilder: (_, int index) => Padding(
padding: const EdgeInsets.all(16.0),
child: _buildMessage(index),
),
),
),
]),
),
Container(
color: Colors.blue,
height: 50.0,
alignment: Alignment.bottomCenter,
child: Center(
child: Text("Composer"),
),
),
],
),
);
}
Text _buildMessage(int index) => Text("Message " + (index + 1).toString());
Container _buildTopHeader() {
return Container(
height: _headerHeight,
color: Colors.pink,
child: Center(
child: Text("Header"),
),
);
}
Container _buildIamge() {
return Container(
color: Colors.grey,
);
}
}

Align ListView's children to top and bottom

TL;DR
Is it possible to align ListView's children like Column's spaceBetween alignment?
Like in this screenshot:
Problem that I tried to solve:
In the beginning I had a Column widget with 2 children. The Column's alignment was set to spaceBetween. It was what I wanted, one widget at the top, one at the bottom. But then when I clicked to input to add credentials, the keyboard caused an overflow issue described in this GitHub issue. So to fix it, I replaced the Column with ListView. But now my children widgets are stuck at the top of the screen.
My full LoginScreen code:
import 'package:flutter/material.dart';
import '../widgets/button.dart';
import '../widgets/input.dart';
import '../widgets/logo.dart';
class LoginScreen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Background(),
Wrapper(),
],
),
);
}
}
class Background extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Color(0xfff6f6f6),
Colors.white,
],
),
),
);
}
}
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Logo(LogoColor.dummy_black),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20),
child: BottomPart(),
),
),
],
);
}
}
class BottomPart extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Input('User name'),
Input('Password'),
Button(
'Log in',
onPressed,
),
],
);
}
void onPressed() {}
}
You could use ListView's property reverse
reverse: true,
And then change the order of the ListView children.
Find a solution. You should wrap your Column in IntrinsicHeight and ConstrainedBox with minHeight.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: IntrinsicHeight(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height,
),
child: Column(
children: [
Container(
height: 100,
width: double.infinity,
color: Colors.yellow,
child: Text('1', style: TextStyle(color: Colors.black)),
),
Spacer(),
Container(
height: 100,
width: double.infinity,
color: Colors.red,
child: Text('2', style: TextStyle(color: Colors.black)),
),
],
),
),
),
);
}
}
Try using SizedBox(height:50.0) // or any other value in b/w Logo widget and Align(bottom part).
return ListView(
children: <Widget>[
Logo(LogoColor.dummy_black),
SizedBox(height: MediaQuery.of(context).size.height/ 3),// you will get value which is 1/3rd part of height of your device screen
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(20),
child: BottomPart(),
),
),
],
);
Finally I found out soln to this problem. Use Code :`
Align(
alignment: Alignment.bottomCenter,
child: ListView(
shrinkWrap: true,
children: <Widget>[
// Your Widget Here
],
),
),
you can insert this between logo and Align
Expanded(child: SizedBox(height: 8)),
You could take out the bottom part from ListView, and put it in the last of Stack's children. Your Wrapper could look like:
Widget build(BuildContext context){
var bottomHeight = 120.0;//replaced with your BottomPart's height
return Stack(
children: [
ListView(),//your ListView
Positioned(
top: MediaQuery.of(context).size.height - bottomHeight,
child: SizedBox(
height: bottomHeight,
child: BottomPart(),
)
)
],
);
}

How to remove Padding from DrawerHeader

Here's my DrawerHeader :
class MyDrawerHeader extends StatefulWidget {
#override
_MyDrawerHeaderState createState() => _MyDrawerHeaderState();
}
class _MyDrawerHeaderState extends State<MyDrawerHeader> {
#override
Widget build(BuildContext context) {
return DrawerHeader(
padding: EdgeInsets.all(0),
margin: EdgeInsets.all(0),
child: Center(child: Text('Header', style: Theme.of(context).textTheme.headline))
);
}
}
As you can see I made the Padding and Margin from the DrawerHeader be 0, but this is how my Header is being shown:
It's just too big and I can't make it smaller. I have no idea why its being rendered this way, I looked into DrawerHeader source code and I can't see anything in there overriding my Padding or Margin.
Just to be sure the problem is in DrawerHeader, this is what Happens when I substitute it for a Container:
It works as it should!
Am I missing something, or is this a bug in Flutter?
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
padding: EdgeInsets.all(0.0),
child: Container(
color: Theme.of(context).primaryColor,
),
),
ListTile(
title: Text("Home"),
)
],
),
),
There is always padding on DrawerHeader. If you look in sources:
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMediaQuery(context));
final ThemeData theme = Theme.of(context);
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Container(
height: statusBarHeight + _kDrawerHeaderHeight,
margin: margin,
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context),
),
),
child: AnimatedContainer(
padding: padding.add(EdgeInsets.only(top: statusBarHeight)),
decoration: decoration,
duration: duration,
curve: curve,
child: child == null ? null : DefaultTextStyle(
style: theme.textTheme.body2,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: child,
),
),
),
);
}
You can customize this code:
height: statusBarHeight + _kDrawerHeaderHeight - here is total height of header
padding: padding.add(EdgeInsets.only(top: statusBarHeight)) - here is padding of child element in DrawerHeader
Try replacing your drawer by the code below
drawer: Drawer(
child: DrawerHeader(
child: ListView(
children: <Widget>[
Container(
alignment: Alignment.topLeft,
child: Text('Header', style: Theme.of(context).textTheme.headline),
),
],
),
),
),