How do I make the widget run sequentially in flutter? - flutter

I have the following code written in a flutter I want to make the pieces run in order (from 1 to the last piece) See the comment on line 34,
how can I Do ?
....
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: _clipsWidget(), // <<< I want to write a code here that allows widgets (widget 1-widget 2....) to be executed in an orderly and sequential manner
),
],
),
),
),
],
),
);
}
Widget _clipsWidget1() {
return Container(
height: 250,
margin: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
....
),
SizedBox(height: 20),
Container(
....
),
}
Widget _clipsWidget2() {.....}
Widget _clipsWidget3() {.....}
Widget _clipsWidget4() {.....}

To have multiple widgets arranged below each other, you can use a Row widget:
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
_clipsWidget1(),
_clipsWidget2(),
_clipsWidget3(),
_clipsWidget4()
]
)
)

Does having to be shown sequentially over time mean that we need animations? Then you should use AnimatedList -> https://api.flutter.dev/flutter/widgets/AnimatedList-class.html

if you need to show first widget for sometime than second widget and so on
then try
class _RunOneByOneState extends State<RunOneByOne> {
final widgets = <Widget>[
_clipsWidget1(),
_clipsWidget2(),
_clipsWidget3(),
_clipsWidget4()
];
int index = 0;
#override
void initState() {
Timer.periodic(const Duration(seconds: 20), (timer) {
if (index != widgets.length - 1) {
setState(() {
index++;
});
} else {
timer.cancel();
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: widgets[index],
);
}
}

Related

Scroll list depending on another list scrolling Flutter

How I can make the scroll of a list depending on another list scrolling for example :
class ConectLists extends StatefulWidget {
const ConectLists({Key? key}) : super(key: key);
#override
State<ConectLists> createState() => _ConectListsState();
}
class _ConectListsState extends State<ConectLists> {
ScrollController scrollConroller1 = ScrollController();
ScrollController scrollConroller2 = ScrollController();
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
void dispose() {
// TODO: implement dispose
scrollConroller1.dispose();
scrollConroller2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SizedBox(
height: 8,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('List 1'),
Text('List 2'),
],
),
SizedBox(
height: 8,
),
Container(
color: Colors.black.withOpacity(0.5),
width: double.infinity,
height: 4,
),
Expanded(
child: Row(
children: [
Expanded(
flex: 1,
child: ListView.builder(
controller: scrollConroller1,
itemBuilder: (context, index) => Card(
elevation: 3,
child: SizedBox(
height: 40,
child:
Center(child: Text('First list item $index')))),
itemCount: 50,
),
),
Container(
color: Colors.black.withOpacity(0.5),
width: 4,
height: double.infinity,
),
Expanded(
child: ListView.builder(
controller: scrollConroller2,
itemBuilder: (context, index) => Card(
elevation: 3,
child: SizedBox(
height: 40,
child: Center(
child: Text('Second list item $index')))),
itemCount: 25,
),
),
],
),
),
],
),
);
}
}
I need to make list 2 scroll when List1 scroll with controlling the speed of the list2 scrolling (different scroll speed) for example or reverse the direction for example..
Is there a lite way to do this in Fultter ?
You can easily achieve this by adding listeners to your ScrollController like so :
controller: scrollConroller1..addListener(() {
scrollConroller2.animateTo(scrollConroller1.offset,
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 450));
}),
Basically you listen on scroll changes and you assign ones' Offset to the other list. However, when the first list's length is larger than the second list,the second list will keep on bouncing at the bottom (on iOS devices). You could fix that by checking if the first list's offset is larger than the second list's maxScrollExtent :
controller: scrollConroller1..addListener(() {
if (scrollConroller1.offset <= scrollConroller2.position.maxScrollExtent){
scrollConroller2.animateTo(scrollConroller1.offset,
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 450));
}
}),
You could add a listener in your init state to make scrollConroller2 jump to the postion scrollConroller1 is at as below.
Credit to esentis for the fix when first list's offset is larger
than the second list's maxScrollExtent :
#override
void initState() {
super.initState();
//Your other code in init state
scrollConroller1.addListener(() {
if (scrollConroller1.offset <=
scrollConroller2.position.maxScrollExtent) {
setState(() {
double value2 = scrollConroller1.offset;
scrollConroller2.jumpTo(value2);
});
}
});
}
To scroll in reverse, you can set the listener instead to:
scrollConroller1.addListener(() {
if (scrollConroller1.offset <=
scrollConroller2.position.maxScrollExtent) {
setState(() {
double value2 = scrollConroller2.position.maxScrollExtent -
scrollConroller1.offset;
scrollConroller2.jumpTo(value2);
});
}
});
To control list scroll while maintaining the scroll offset that will be based on height ratio, Therefore the jump offset will be
jumpPoss = (math.min(l1maxHeight, l2maxHeight) * scrollOffset) /
math.max(l1maxHeight, l2maxHeight);
late ScrollController scrollController1 = ScrollController()
..addListener(() {
double scrollOffset = scrollController1.offset;
final double l1maxHeight = scrollController1.position.maxScrollExtent;
final double l2maxHeight = scrollController2.position.maxScrollExtent;
double jumpPoss = (math.min(l1maxHeight, l2maxHeight) * scrollOffset) /
math.max(l1maxHeight, l2maxHeight);
scrollController2.jumpTo((jumpPoss));
});
You can follow #Tonny Bawembye's answer if you need to stop scrolling on max limit.

RefreshIndicator not working with Scrollview

I am trying to use RefreshIndicator with Scrollview as want extra widgits too with list in RefreshIndicator but it is not working.
Basically I want to use RefreshIndicator then inside it a Scrollview with a column with multiple widgets.
Thanks in advance!
Here is my code:
class _DashboardState extends State<Dashboard> {
List<String> _demoData = [];
#override
void initState() {
_demoData = [
"Flutter",
"React Native",
"Cordova/ PhoneGap",
"Native Script"
];
super.initState();
}
#override
Widget build(BuildContext context) {
Future doRefresh() {
return Future.delayed(
Duration(seconds: 0),
() {
setState(() {
print("refresh worked");
_demoData.addAll(["Ionic", "Xamarin"]);
});
},
);
}
return RefreshIndicator(
onRefresh: doRefresh,
child: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: 400,
child: ListView.builder(
itemBuilder: (ctx, idx) {
return Card(
child: ListTile(
title: Text(_demoData[idx]),
),
);
},
itemCount: _demoData.length,
),
),
],
),
),
);
}
}
The problem is that your list that has the 'RefreshIndicator' is in another list, this only scrolls the parent list and the child list where the 'RefreshIndicator' is located will not work, delete the parent list and have it only display the child list like the following example:
Another problem is also that the 'doRefresh' method is inside the 'build' method of the Widget, take it out of the 'build' method like the example: (in seconds put 2 or 3 seconds to see the animation)
class _DashboardState extends State<Dashboard> {
List<String> _demoData = [];
#override
void initState() {
_demoData = [
"Flutter",
"React Native",
"Cordova/ PhoneGap",
"Native Script"
];
super.initState();
}
Future doRefresh() {
return Future.delayed(
Duration(seconds: 3),
() {
setState(() {
print("refresh worked");
_demoData.addAll(["Ionic", "Xamarin"]);
});
},
);
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: doRefresh,
child: ListView.builder(
itemBuilder: (ctx, idx) {
return Card(
child: ListTile(
title: Text(_demoData[idx]),
),
);
},
itemCount: _demoData.length,
));
}
}
If you want to add more widgets, those widgets have to be inside the ListView, if you want to use more lists it is better to use a CustomScrollView instead of the ListView
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: doRefresh,
child: ListView(
children: [
...List.generate(
_demoData.length,
(idx) => Card(
child: ListTile(
title: Text(_demoData[idx]),
),
)),
Container(
height: 100,
width: double.infinity,
color: Colors.red,
),
...List.generate(
_demoData.length,
(idx) => Card(
child: ListTile(
title: Text(_demoData[idx]),
),
)),
],
));
}

Flutter: ScrollController initialScrollOffset not working

I'm trying to initialize a SingleChildScrollView to start at a certain position with a custom ScrollController. I thought I could use initialScrollOffset and set an initial value in the init method. But somehow when the SingleChildScrollView renders, it only jumps to initialOffset at first build, then when I navigate to another instance of this Widget it doesn't jump to the initialOffset position.
I don't know why, and if I'm lucky maybe one of you have the answer.
Here's my code:
class Artiklar extends StatefulWidget {
final String path;
final double arguments;
Artiklar({
this.path,
this.arguments,
});
#override
_ArtiklarState createState() => _ArtiklarState(arguments: arguments);
}
class _ArtiklarState extends State<Artiklar> {
final double arguments;
_ArtiklarState({this.arguments});
ScrollController _scrollController;
double scrollPosition;
#override
void initState() {
super.initState();
double initialOffset = arguments != null ? arguments : 22.2;
_scrollController = ScrollController(initialScrollOffset: initialOffset);
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final bool isAdmin = Provider.of<bool>(context) ?? false;
var pathElements = widget.path.split('/');
String tag;
if (pathElements.length == 2) {
tag = null;
} else if (pathElements.length == 3) {
tag = pathElements[2];
} else {
tag = null;
}
return StreamBuilder<List<ArtikelData>>(
stream: DatabaseService(tag: tag).artiklarByDate,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return GlobalScaffold(
body: SingleChildScrollView(
child: Container(
child: Center(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
GradientHeading(text: "Artiklar", large: true),
isAdmin
? NormalButton(
text: "Skapa ny artikel",
onPressed: () {
Navigator.pushNamed(
context, createNewArtikelRoute);
},
)
: Container(),
SizedBox(height: 10),
SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: TagList(path: tag),
),
SizedBox(height: 10),
LatestArtiklar(
snapshot: snapshot,
totalPosts: snapshot.data.length,
numberOfPosts: 10,
),
],
),
),
),
),
),
);
} else if (!snapshot.hasData) {
return GlobalScaffold(
body: SingleChildScrollView(
child: Container(
child: Center(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
GradientHeading(text: "Artiklar", large: true),
SizedBox(height: 10),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: TagList(path: tag),
),
SizedBox(height: 10),
LatestArtiklar(hasNoPosts: true)
],
),
),
),
),
),
);
} else {
return GlobalScaffold(
body: Center(child: CircularProgressIndicator()),
);
}
},
);
}
}
That's because that widget is already built on the tree and thus, initState won't be called again for that widget.
You can override the didUpdateWidget method that will trigger each time that widget is rebuilt and make it jump on there, for example.
#override
void didUpdateWidget(Widget old){
super.didUpdateWidget(old);
_scrollController.jumpTo(initialOffset);
}
keepScrollOffset: false
If this property is set to false, the scroll offset is never saved and initialScrollOffset is always used to initialize the scroll offset.

Flutter Error: 'indexOf(child) > index': is not true. (StreamBuilder,PageView)

I'm trying to create a screen that is contained within a pageview, that also contains a page view for part of the screen.
To acheive this I have an unlimited page view for the whole page itself, then every page has a header view, with a bottom half that has a page view with 3 possible options. I have this pretty much working, however, the pages I am using I would like a StreamBuilder... This is where the issue is caused.
class DiaryPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _DiaryPage();
}
class _DiaryPage extends State<DiaryPage> with TickerProviderStateMixin {
DiaryBloc _diaryBloc;
TabController _tabController;
PageController _pageController;
#override
void initState() {
_diaryBloc = BlocProvider.of<DiaryBloc>(context);
_diaryBloc.init();
_tabController = TabController(length: 3, vsync: this);
_pageController = PageController(initialPage: _diaryBloc.initialPage);
super.initState();
}
#override
void dispose() {
_diaryBloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Flexible(
child: PageView.builder(
controller: _pageController,
itemBuilder: (BuildContext context, int position) {
return _buildPage(_diaryBloc.getDateFromPosition(position));
},
itemCount: _diaryBloc.amountOfPages,
),
);
}
Widget _buildPage(DateTime date) {
return Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[_getHeader(date), _getTabBody()],
);
}
Widget _getHeader(DateTime date) {
return Card(
child: SizedBox(
width: double.infinity,
height: 125,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8, 16, 8, 0),
child: Text(
'${DateFormat('EEEE').format(date)} ${date.day} ${DateFormat('MMMM').format(date)}',
style: Theme.of(context).textTheme.subtitle,
textScaleFactor: 1,
textAlign: TextAlign.center,
),
),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: () => {
_pageController.previousPage(
duration: Duration(milliseconds: 250),
curve: Curves.ease)
},
),
const Expanded(child: LinearProgressIndicator()),
IconButton(
icon: const Icon(Icons.chevron_right),
onPressed: () => {
_pageController.nextPage(
duration: Duration(milliseconds: 250),
curve: Curves.ease)
},
),
],
),
Container(
height: 40.0,
child: DefaultTabController(
length: 3,
child: Scaffold(
backgroundColor: Colors.white,
appBar: TabBar(
controller: _tabController,
unselectedLabelColor: Colors.grey[500],
labelColor: Theme.of(context).primaryColor,
tabs: const <Widget>[
Tab(icon: Icon(Icons.pie_chart)),
Tab(icon: Icon(Icons.fastfood)),
Tab(icon: Icon(Icons.directions_run)),
],
),
),
),
),
],
),
),
);
}
Widget _getTabBody() {
return Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
_getOverviewScreen(),
_getFoodScreen(),
_getExerciseScreen(),
],
),
);
}
// TODO - this seems to be the issue, wtf and why
Widget _getBody() {
return Flexible(
child: StreamBuilder<Widget>(
stream: _diaryBloc.widgetStream,
initialData: _diaryBloc.buildEmptyWidget(),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
return snapshot.data;
},
),
);
}
Widget _getExerciseScreen() {
return Text("Exercise Screen"); //_getBody();
}
Widget _getFoodScreen() {
return Text("Food Screen"); //_getBody();
}
Widget _getOverviewScreen() {
return _getBody();
}
}
As you can see, there are three widgets being returned as part of the sub page view, 2 of them are Text Widgets which show correctly, but the StreamBuilder, which is populated correctly with another Text Widget seems to give me the red screen of death. Any ideas?
Fixed the problem, it was related to the StreamBuilder being wrapped in a Flexible rather than a column. I then added column to have a mainAxisSize of max... Seemed to work.
For custom ListView/PageView
In my case, I wanted to clear the list of my listview. In a custom ListView/PageView, the findChildIndexCallback will find the element's index after i.e. a reordering operation, but also when you clear the list.
yourList.indexWhere()unfortunately returns -1 when it couldn't find an element. So, Make sure to return null in that case, to tell the callback that the child doesn't exist anymore.
...
findChildIndexCallback: (Key key) {
final ValueKey<String> valueKey = key as ValueKey<String>;
final data = valueKey.value;
final index = images.indexWhere((element) => element.id == data);
//important here:
if (index > 0 ) return index;
else return null;
},

how to give my list view width to full screen width and how to make it auto scroll

how to give my list view width to full screen width and how to make it auto scroll after few second ...
void scrollAfter(ScrollController scrollController, {int seconds}) {
Future.delayed(Duration(seconds: seconds), () {
var offset = 550.0;
var scrollDuration = Duration(seconds: 2);
scrollController.animateTo(offset,
duration: scrollDuration, curve: Curves.ease);
});
}
#override
Widget build(BuildContext context) {
var scrollController = ScrollController();
scrollAfter(scrollController, seconds: 2);
// TODO: implement build
return Container(child:
new StreamBuilder(
stream: Firestore.instance.collection('Top List').snapshots(),
builder: (BuildContext context,snapshot) {
if (!snapshot.hasData) return new Text("no");
var documentsLength = snapshot.data.documents.length;
return ListView.builder(itemCount: documentsLength,
scrollDirection: Axis.horizontal,
controller: scrollController,
shrinkWrap: true,
itemBuilder: (context, index) {
return buildlistItem((AllProduct.fromDocument(snapshot.data.documents[index])));
});
below my buildlistItem class form which i want to scroll ######################################################################################################################################################################################################################################################################
Widget buildlistItem(AllProduct alllist) {
return
new GestureDetector(
child: Container(
child: new Card(
elevation: 2.0,
margin: const EdgeInsets.all(5.0),
child: new Stack(
alignment: Alignment.center,
children: <Widget>[
new Hero(
tag: alllist.title,
child: new Image.network(alllist.backgroundImageUrl, fit: BoxFit.cover),
),
new Align(
child: new Container(
padding: const EdgeInsets.all(6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(alllist.title,
style: new TextStyle(color: Colors.white,fontFamily: "ChelaOne-Regular")),
],
),
IconButton(onPressed: (){
},
icon: new Icon(Icons.add_shopping_cart,color: Colors.white,),
)
],
),
color: Colors.black.withOpacity(0.4),
),
alignment: Alignment.bottomCenter,
),
],
),
),
),
onTap: () {},
);
}
}
You can use ScrollController to achieve the auto-scrolling part. Please refer the below example.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
var scrollController = ScrollController();
var listView = ListView.builder(
itemBuilder: (context, index) => Text("$index"),
controller: scrollController,
);
scrollAfter(scrollController, seconds: 2);
return MaterialApp(
title: 'Trial',
home: Scaffold(
appBar: AppBar(title: Text('List scroll')), body: listView));
}
void scrollAfter(ScrollController scrollController, {int seconds}) {
Future.delayed(Duration(seconds: seconds), () {
var offset = 550.0;
var scrollDuration = Duration(seconds: 2);
scrollController.animateTo(offset,
duration: scrollDuration, curve: Curves.ease);
});
}
}
You can use Column or Row Widget to align children vertically or horizontally. Regrading expanding list content to to full width you can use Expanded widget.
Achieving automatic scrolling is pretty simple. ListView widget accepts a parameter of controller: in its constructor.
final ScrollController _scrollController = new ScrollController();
Achieving full width and auto scroll.
new Column(
children: [
new Expanded(
new ListView.builder(builder:(BuildContext _context, int index){
return //your list elements here.
},
controller: _scrollController,
itemCount: count //total count of elements on list
),
),
// your other widgets here.
]
)
for auto scroll
//your logic here
_scrollController.animateTo(double position, Duration duration, Curves curve);//refer to documentation