Flutter: Horizontal Stepper - auto scrolling - flutter

As you may know, the Stepper widget puts step items into a row until it overflows the screen, with no way to make it scroll.
However, I found a way found in this link - https://github.com/flutter/flutter/issues/40601
Here is the code:
Widget _buildHorizontal() {
final List children = [
for (int i = 0; i < widget.steps.length; i += 1) ...[
InkResponse(
onTap: widget.steps[i].state != StepState.disabled
? () {
if (widget.onStepTapped != null) widget.onStepTapped(i);
}
: null,
canRequestFocus: widget.steps[i].state != StepState.disabled,
child: Row(
children: [
Container(
height: 72.0,
child: Center(
child: _buildIcon(i),
),
),
Container(
margin: const EdgeInsetsDirectional.only(start: 12.0),
child: _buildHeaderText(i),
),
],
),
),
if (!_isLast(i))
Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
height: 1.0,
color: Colors.grey.shade400,
),
],
];
return Column(
children: <Widget>[
Material(
elevation: 2.0,
child: Container(
height: 65,
margin: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListView(
scrollDirection: Axis.horizontal,
children: children,
),
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(24.0),
children: <Widget>[
AnimatedSize(
curve: Curves.fastOutSlowIn,
duration: kThemeAnimationDuration,
vsync: this,
child: widget.steps[widget.currentStep].content,
),
_buildVerticalControls(),
],
),
),
],
);
}`
My issue right now is that I cannot make the ListView autoscrollable.
I have tried to use _scrollController = ItemScrollController but did not help.

Related

Flutter add bottom margin to Stack

I am trying to extend Stack a little bit down to create room for "All caught up" text.
Here is my code;
return Stack(
children: [
buildListView(scrollController),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
Widget buildBackToTop(ScrollController scrollController, bool backtoTop) {
if (backtoTop) {
return Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton.extended(
onPressed: () {
scrollController.animateTo(0,
duration: const Duration(milliseconds: 200),
curve: Curves.linear);
},
label: const Text("Back to Top"),
),
),
);
} else {
return const SizedBox();
}
}
Widget buildBottomReached(bool isLastIndex) {
if (isLastIndex) {
return const Align(
alignment: Alignment.bottomCenter,
child: Text(
"All caught up 🎉",
style: TextStyle(
color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 20.00),
),
);
} else {
return const SizedBox();
}
}
Widget buildListView(ScrollController scrollController) {
return ListView.builder(
controller: scrollController,
itemCount: 50,
itemBuilder: (context, index) {
return SizedBox(
width: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 20,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100],
),
child: Center(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Index $index"),
),
),
),
),
),
);
},
physics: const BouncingScrollPhysics());
}
When I do following,
return Stack(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: buildListView(scrollController),
),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
This is desired but it adds whitespace to bottom all the time,
Is there a way to do this?
another valid solution for your case is to expand the Stack to the screen size, then align your widgets with the Align widget like this:
final mq = MediaQuery.of(context);
return ConstrainedBox(
constraints: BoxConstraints(
maxHeight: mq.size.height,
minHeight: mq.size.height,
maxWidth: mq.size.width,
minWidth: mq.size.width,
),
child: Stack(
fit: StackFit.expand,
children: [
buildListView(scrollController),
Align(
alignment: Alignment.bottomCenter,
child: buildBackToTop(scrollController, backtoTop),
),
buildBottomReached(isLastIndex),
],
),
);
Based on your UI. try Column widget instead of Stack.
return Column(
children: [
Expaneded(child: buildListView(scrollController)),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
If it needs overlay, use Stack by combining listView and Text with Column.
Wrap the widget with Positioned and add bottom.
return Stack(
children: [
.....
Positioned(
bottom: yourValue,
child:
First, wrap Stack with ListView widget
Then, move buildBottomReached helper as a sibling of the Stack widget
return ListView(//this
children: [
Stack(
children: [
buildListView(),
buildBackToTop(backtoTop),
//buildBottomReached(isLastIndex),//move this a sibling of the Stack
],
),
buildBottomReached(isLastIndex),//moved here
],
);
Then, on buildListView helper, set ListView.builder property shrinkWrap to true.
Widget buildListView(ScrollController scrollController) {
return ListView.builder(
shrinkWrap: true,//this
controller: scrollController,
itemCount: 50,
itemBuilder: (context, index) {
return SizedBox(
width: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 20,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100],
),
child: Center(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Index $index"),
),
),
),
),
),
);
},
physics: const BouncingScrollPhysics());
}

Can anyone help to make this precise custom Widget. It's really challenging. I spend hours and couldn't complete it

This is what i am looking for. what i could done is this.
It will be great if someone could help.
In below code i use getx for state change and some custom internal custom widgets.
Adding more info for those who are not familiar with Getx
The obx here in the code is just a responsive widget to change state
`Container(
decoration: ContainerStyles(radius: 7, color: ColorConst.grey6)
.borderRadiusCustom(),
child: Obx(
() => Row(
children: [
SizedBox(
width: 100,
child: NotificationListener(
child: Stack(
alignment: Alignment.centerLeft,
children: [
ListView(
controller: controller.scrollController,
padding: const EdgeInsets.only(left: 16),
scrollDirection: Axis.vertical,
children: List.generate(
100,
(index) => Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
AnimatedContainer(
duration:
const Duration(milliseconds: 500),
alignment: Alignment.bottomCenter,
margin: const EdgeInsets.symmetric(
horizontal: 4.5),
width: /*(controller.scrollCount() ==
index / 5
? 14
:*/ (index % 5) == 0
? 38
: 14,
height: 1,
color: ColorConst.black,
),
Text(
((index % 5) == 0
? index~/5
: "").toString() ,
style: Theme.of(context)
.textTheme
.subtitle2),
],
)).toList(),
),
Transform.translate(
offset: const Offset(16, -10),
child: Container(
height: 1.3,
width: 70,
color: ColorConst.black,
),
)
],
),
onNotification: (_) {
int calculatedIndex =
((controller.scrollController.position.pixels +
controller.scrollController.position
.viewportDimension /
2) /
50)
.floor();
if (calculatedIndex != controller.scrollCount()) {
controller.scrollCount.value = calculatedIndex;
print(
'center item = ${controller.scrollCount.value}');
} else {
print(controller.scrollCount.value);
}
return true;
},
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
margin: const EdgeInsets.only(bottom: 20),
decoration:
ContainerStyles(radius: 5, color: ColorConst.tertiary1)
.borderRadiusCustom(),
child: Text(
"${controller.scrollCount().toInt()}",
style: Theme.of(context)
.textTheme
.button
?.changeSize(30)
.white(),
),
),
],
),
),
),`
i am expecting a out like the image i provided here.

Lazy scrolling with silvers

I want lazy scrolling when my all page is scrolled so i have to use Custom Scroll View.
now CustomScrollView can have just silvers children, my problem is have many lists and widgets,
expanded widget that has his own list.
to achieve the best lazy scrolling i have to some how get to top level widget all my lists and that can become very nasty.
LeagueGamesList widget contain list of expanded widgets that every expanded widget is a list.
how i can do it and still keep order and maintain widgets?
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return Container(
padding: EdgeInsets.only(left: 15),
child: Column(
children: <Widget>[
if (_highlights.length > 0)
Column(
children: [
Container(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Row(
children: [
Text(
'Highlights',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
Image.asset(
AppIcons.rightArrow,
alignment: Alignment.center,
width: 20,
height: 20,
)
],
),
),
),
),
Container(
child: Align(
alignment: Alignment.centerLeft,
child: HighlightRow(highlights: _highlights),
),
),
],
),
Visibility(
visible: _advertisements.length != 0,
child: Padding(
padding: EdgeInsets.only(right: 15),
child: HomepageBanner(
advertisement: _homepageAdvertisement,
),
),
),
if (Provider.of<ArticlesProvider>(context).get().length > 0)
HomepageArticles(
articles: Provider.of<ArticlesProvider>(context).get(),
),
],
),
);
}, childCount: 1),
),
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return Column(
children: [
Visibility(
visible: isLoading,
child: SpinKitFadingCircle(
color: Colors.black,
size: 20,
),
replacement: SizedBox(height: 20),
),
DateButton(
dateString: getDateString(),
dayForwards: dayForwards,
dayBackwards: dayBackwards,
),
SizedBox(height: 20),
],
);
}, childCount: 1),
),
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
print(index);
return LeagueGamesList(
leagueFixture: leagueFixtures[index],
closeExpanders: false,
openExpanders: isDateChanged,
rowHeight: rowHeight,
);
}, childCount: leagueFixtures.length),
),
],
)
my expanded container that will contain a list
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double collapsedHeight;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.collapsedHeight = 0.0,
this.expandedHeight = 0,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : collapsedHeight,
padding: EdgeInsets.all(0),
margin: EdgeInsets.all(0),
alignment: Alignment.topLeft,
child: child,
);
}
Instead of nesting lists in lists make only the deepest list be actual SliverLists and combine them together using MultiSlivers from my package sliver_tools

Flutter ListView not scrolling (I feel like I've tried every solution on the internet)

If I drag and hold my finger down I can see a few items that are below the cutoff of the screen but as soon as I let go, it just bounces back to the top. I tried using SingleChildScrollView places, tried setting primary = true, and a bunch of other stuff that didn't help. I'm fairly new to flutter so any help would be appreciated!! Let me know if any more info is needed.
Here is my code:
import 'package:flutter/material.dart';
import 'package:drink_specials/models/restaurant.dart';
import 'package:drink_specials/screens/home/restaurant_list.dart';
class RestaurantNameTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline2.copyWith(color: Colors.white);
}
}
class RestaurantTypeTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline6.copyWith(color: Colors.white);
}
}
class RestaurantDetail extends StatelessWidget {
final Restaurant restaurant;
RestaurantDetail({Key key, #required this.restaurant}) : super(key: key);
#override
Widget build(BuildContext context) {
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 100.0),
Text(
restaurant.name,
style: RestaurantNameTextStyle.display5(context),
),
SizedBox(height: 10.0),
Expanded(
flex: 6,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(
restaurant.restaurant_type,
style: RestaurantTypeTextStyle.display5(context),
))),
],
);
final topContent = Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10.0),
height: MediaQuery.of(context).size.height * 0.5,
decoration: new BoxDecoration(
image: new DecorationImage(
image: NetworkImage(restaurant.photo),
fit: BoxFit.cover,
),
)),
Container(
height: MediaQuery.of(context).size.height * 0.5,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
final bottomContent = Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Center(
child: ListView.builder(
scrollDirection: Axis.vertical,
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemCount: restaurant.specials.length,
itemBuilder: (context, index) {
final item = restaurant.specials[index];
return Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, 1.0)),
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal:20, vertical:10),
title: Text(item, style: TextStyle(color: Colors.white)),
)
),
);
}
),
),
);
return Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
}
}
There is a ListView inside a SingleChildScrollView and both of them are scrollable. Scrolling on one of them should be disabled.
As they already explained. If you have a ListView.builder, you don't need SingleChildScrollView.
Try removing SingleChildScrollView. The code should look like this:
Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
ListView already have scroll behavior so you won't need some SingleChildScrollView

What is best way to remove overflowing by a lot of pixels in Flutter?

I have Stepper in my app, and I have problems with placing textfield on screen, when I want to text some text in textfield, appears keyboard and over it shows me that:
A RenderFlex overflowed by 139 pixels on the bottom.
I read some articles and understood, that I have to use FittedBox, but I dunno how to use it with best way. How can I reach my goal?
Code:
#override
Widget build(BuildContext context) {
globalHeight = (MediaQuery.of(context).size.height) * 0.85;
return Scaffold(
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: Container(
decoration: BoxDecoration(color: colorsBackround[_currentPage]),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
height: globalHeight,
child: PageView(
physics: ClampingScrollPhysics(),
controller: _pageController,
onPageChanged: (int page) {
setState(() {
_currentPage = page;
});
},
children: <Widget>[
// some code
Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Image(
image: AssetImage(
itemIcon[_currentPage],
),
height: 300.0,
width: 300.0,
),
Text(
'Укажите ваш возраст',
style: kTitleStyle,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
height: 50.0,
child: Padding(
padding: EdgeInsets.only(
top: 20.0,
left: 20,
right: 20,
bottom: MediaQuery.of(context)
.viewInsets
.bottom),
child: TextField(
controller: ageController,
keyboardType: TextInputType.number,
onChanged: (text) {
setState(() {
if (text.isNotEmpty) {
inputs[1] = true;
} else {
inputs[1] = false;
}
});
},
decoration: InputDecoration(
labelText: 'Возраст',
),
style: TextStyle(fontSize: 18.5),
)),
),
],
),
),
),
//some code
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _buildPageIndicator(),
),
_currentPage != _numPages - 1
? Expanded(
child: Container(
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.bottomLeft,
child: FlatButton(
onPressed: () {
_pageController.previousPage(
duration: Duration(milliseconds: 500),
curve: Curves.ease,
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.arrow_back,
color: Colors.white,
size: 26.0,
),
SizedBox(width: 10.0),
Text(
'Назад',
style: TextStyle(
fontFamily: 'Century Gothic',
color: Colors.white,
fontSize: 14.5,
),
),
],
),
),
)),
Expanded(
child: Align(
alignment: FractionalOffset.bottomRight,
child: FlatButton(
onPressed: () {
_pageController.nextPage(
duration: Duration(milliseconds: 500),
curve: Curves.ease,
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Дальше',
style: TextStyle(
fontFamily: 'Century Gothic',
color: Colors.white,
fontSize: 14.5,
),
),
SizedBox(width: 10.0),
Icon(
Icons.arrow_forward,
color: Colors.white,
size: 26.0,
),
],
),
),
)),
],
)))
: Text(''),
],
),
),
),
),
bottomSheet: _currentPage == _numPages - 1
? Container(
height: 75.0,
width: double.infinity,
color: Theme.of(context).scaffoldBackgroundColor,
child: GestureDetector(
onTap: () => print('Get started'),
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Text(
'Начать',
style: TextStyle(
fontFamily: 'Century Gothic',
color: Colors.white,
fontSize: 21.0,
fontWeight: FontWeight.bold,
),
),
),
),
),
)
: Text(''),
);
}
}
There is no direct solution to prevent overflowing issues, it depends on your current code. So, here you use
Add to your Scaffold
resizeToAvoidBottomInset: false
Wrap your widget in SingleChildScrollView
SingleChildScrollView(
child: YourColumn(),
)
That happens because when opening the keyboard, the body is resized to avoid the keyboard appear over the text field, and since your content isn't scrollable the content of the body gets overflowed. Check this property of the Scaffold:
/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final bool resizeToAvoidBottomInset;
If you put that to false, the body won't be resized so the content won't be overflowed.
If you leave it as default, you need to make the body scrollable. In your case, you could change the root Column for a ListView and you will need to remove the Expanded wrap of the third item of the column.
But I recommend you to try to simplify the structure of the widgets.
Wrap your widget with a SingleChildScroll widget and that should work and solve the overflow issue