how to scroll through the two views combined in flutter - flutter

In the video and picture below, the horizontal and vertical widgets are arranged in order.
If you scroll through this, each widget will move separately, just like a video.
I want to make this move at once.
please enter the videoLink
https://firebasestorage.googleapis.com/v0/b/coody-f21eb.appspot.com/o/%E1%84%92%E1%85%AA%E1%84%86%E1%85%A7%E1%86%AB%20%E1%84%80%E1%85%B5%E1%84%85%E1%85%A9%E1%86%A8%202020-09-28%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.06.33.mov?alt=media&token=8a9d3fd0-1256-4d92-9a57-
please enter Imglink
https://firebasestorage.googleapis.com/v0/b/coody-f21eb.appspot.com/o/KakaoTalk_Photo_2020-09-28-08-15-13.jpeg?alt=media&token=77cd7fba-5b62-4d68-b760-8
import 'package:flutter/material.dart';
import 'element_homepage/contents_carousel.dart';
import 'element_homepage/gridView_of_homepage.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'product_detail.dart';
class HomeScreen extends StatefulWidget {
var stopTrigger = 1;
var unchanging ;
List<bool>bool_list_each_GridSell =[];
List<String> styleList = [];
var tf_copy = [];
final FirebaseUser user;
HomeScreen(this.user);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
void initState() {
super.initState();
if(widget.stopTrigger == 1){
setState(() {
widget.unchanging = Firestore.instance.collection("uploaded_product").snapshots();
});
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
appBar: AppBar(title:Text("logo --- rec --- menu")),
body: _bodyBuilder()
),
);
}
Widget _bodyBuilder() {
return Column(
children: [
ContentsCarousel(),
_gridBuilder()
],
);
}
Widget _gridBuilder() {
return Expanded(
child: StreamBuilder <QuerySnapshot>(
stream: _commentStream(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(!snapshot.hasData){
return Center(child: CircularProgressIndicator());
}
var items = snapshot.data?.documents ??[];
var fF = items.where((doc)=> doc['style'] == "오피스룩").toList();
var sF = items.where((doc)=> doc['style'] == "로맨틱").toList();
var tF = items.where((doc)=> doc['style'] == "캐주얼").toList();
fF.addAll(sF);
fF.addAll(tF);
widget.tf_copy.addAll(fF);
if(widget.stopTrigger == 2 ){
fF.shuffle();
widget.unchanging = fF;
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.6,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,),
itemCount: fF.length,
itemBuilder: (BuildContext context, int index) {
for(var i=0; i<fF.length; i++){
widget.bool_list_each_GridSell.add(false);
}
return _buildListItem(context,widget.unchanging[index]);
}
);
},
),
);
}
Widget _buildListItem(context, document) {
return
InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context){
return ProductDetail(widget.user, document);
}));
},
child: Image.network(
document['thumbnail_img'],
fit : BoxFit.cover)
);
}
Stream<QuerySnapshot> _commentStream() {
widget.stopTrigger +=1;
if(widget.stopTrigger == 2 ){
return widget.unchanging;
}
}
}

I see you're attempting to achieve a behavior where a scroll on the GridView results in a scroll on the whole screen.
As the ContentsCarousel() and _gridBuilder() are in a Column, this behaviour cannot be achieved.
What I would suggest is wrapping your Column with a SingleChildScrollView widget.

Related

How to change width of datacells dynamically in flutter DataTable?

I have found in this link a way to place data from JSON dynamically to DataTable() widget:
Dynamically display JSON data in data table layout in flutter
In that post he has ColumnWidths in the json but he doesn't show how he changes width of the individual datacell columns.
I have used that example for myself as well and I wonder how I can use the column widths from json as well to change width of individual data cells?
This is the json I am using:
[
{
"table-data": [
{
"table-label-data": "SL.´Customer´Balance Qty´Amount´Oldest / Recent ",
"table-row-list": [
{
"table-row-data": "1. ´ABD ´14 / 14.60´11,090´313 / 313"
},
{
"table-row-data": "1. ´ABD ´14 / 14.60´11,090´313 / 313"
}
],
"table-cell-widths": "40´168´96´96´108"
}
]
}
]
This is the model I am using:
import 'dart:ui';
class TableModel {
TableModel(this.labelData, this.rowData);
List<String> labelData;
List<List<String>> rowData;
factory TableModel.fromJson(Map<String, dynamic> json) {
return TableModel(
json['table-data'][0]["table-label-data"].split('´').toList(),
buildRowData(json),
);
}
}
List<List<String>> buildRowData(Map<String, dynamic> json) {
List<List<String>> rowDataCollection = [];
json['table-data'][0]["table-row-list"].forEach((rows) {
rowDataCollection.add(rows['table-row-data'].split('´').toList());
});
return rowDataCollection;
}
For the view as you can see i use a variable width inside SizedBox widget to change width, but it is now changing all widths but i want to change individual data cell widths based on json.
This is the view:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:test_project/controllers/getters/get_test.dart';
import '../models/table_model.dart';
import '../models/test_model.dart';
class AppView2 extends StatefulWidget {
const AppView2({
Key? key,
}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView2> {
Future<void> generateList() async {
String responseBody =
await rootBundle.loadString("assets/tableJsonData.json");
var list = await json.decode(responseBody).cast<Map<String, dynamic>>();
return await list
.map<TableModel>((json) => TableModel.fromJson(json))
.toList();
}
#override
void initState() {
generateList();
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('DataTable'),
),
body: FutureBuilder(
future: generateList(),
builder: (context, AsyncSnapshot snapShot) {
if (snapShot.data == null ||
snapShot.connectionState == ConnectionState.waiting ||
snapShot.hasError ||
snapShot.data.length == 0) {
return Container(
child: Center(child: CircularProgressIndicator()),
);
} else {
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapShot.data.length,
itemBuilder: (BuildContext context, int index) {
final TableModel table = snapShot.data[index];
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: table.labelData.map<DataColumn>((e) {
var columnName = e;
return DataColumn(
label: Text(
columnName,
));
}).toList(),
rows: table.rowData.map<DataRow>((e) {
return DataRow(
cells: e.map<DataCell>((e) {
var dataCell = e;
dynamic width;
return DataCell(SizedBox(
width: width,
child: Text(
dataCell,
),
));
}).toList());
}).toList(),
),
);
});
}
},
)));
}
}

How to automatically scroll through all the ListTiles in the Listview.seperated in Flutter?

Scroll automatically (without any user interaction) through all the ListTiles in the Listview using a Timer in flutter. The below method makes only one ListTile to animate but I want to animate all the ListTiles from top to bottom one by one and again from bottom to top one by one.
The below is the Listview:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchNews(),
builder: (context, snap) {
if (snap.hasData) {
news = snap.data;
return ListView.separated(
//controller: _controller,
scrollDirection: scrollDirection,
controller: controller,
itemBuilder: (context, i) {
final NewsModel _item = news[i];
return AutoScrollTag(
key: ValueKey(i),
controller: controller,
index: i,
child: ListTile(
title: Text('${_item.title}'),
subtitle: Text(
'${_item.description}',
// maxLines: 1,
//overflow: TextOverflow.ellipsis,
),
),
);
},
separatorBuilder: (context, i) => Divider(),
itemCount: news.length,
);
} else if (snap.hasError) {
return Center(
child: Text(snap.error.toString()),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
This is the automatic scrolling i have tried:
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 2), (Timer t) async {
await controller.scrollToIndex(1,
preferPosition: AutoScrollPosition.begin);
});
Here is a solution assuming that all your items in the ListView have the same itemExtent.
In this solution, I highlight the current Item as selected. You could also want to stop autoscrolling as soon as you reach the bottom of the list.
Full source code
import 'dart:async';
import 'package:faker/faker.dart';
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part '66455867.auto_scroll.freezed.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
Future<List<News>> _fetchNews() async => dummyData;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('News')),
body: FutureBuilder(
future: _fetchNews(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return NewsList(newsList: snapshot.data);
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class NewsList extends StatefulWidget {
final List<News> newsList;
const NewsList({
Key key,
this.newsList,
}) : super(key: key);
#override
_NewsListState createState() => _NewsListState();
}
class _NewsListState extends State<NewsList> {
ScrollController _scrollController = ScrollController();
Timer _timer;
double _itemExtent = 100.0;
Duration _scrollDuration = Duration(milliseconds: 300);
Curve _scrollCurve = Curves.easeInOut;
int _autoScrollIncrement = 1;
int _currentScrollIndex = 0;
#override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 2), (_) async {
_autoScrollIncrement = _currentScrollIndex == 0
? 1
: _currentScrollIndex == widget.newsList.length - 1
? -1
: _autoScrollIncrement;
_currentScrollIndex += _autoScrollIncrement;
_animateToIndex(_currentScrollIndex);
setState(() {});
});
}
void _animateToIndex(int index) {
_scrollController.animateTo(
index * _itemExtent,
duration: _scrollDuration,
curve: _scrollCurve,
);
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return ListView(
controller: _scrollController,
itemExtent: _itemExtent,
children: widget.newsList
.map((news) => ListTile(
title: Text(news.title),
subtitle: Text(
news.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
selected: widget.newsList[_currentScrollIndex].id == news.id,
selectedTileColor: Colors.amber.shade100,
))
.toList(),
);
}
}
#freezed
abstract class News with _$News {
const factory News({int id, String title, String description}) = _News;
}
final faker = Faker();
final dummyData = List.generate(
10,
(index) => News(
id: faker.randomGenerator.integer(99999999),
title: faker.sport.name(),
description: faker.lorem.sentence(),
),
);
Packages used in the solution:
freeze for the News Domain Class
build_runner to generate the freezed code
faker to generate the list of random news
UPDATE : Scroll only once
To stop the autoscrolling at the bottom of the listview, you just need to modify the initState method:
int _currentScrollIndex;
News _selectedNews;
#override
void initState() {
super.initState();
_currentScrollIndex = -1;
_timer = Timer.periodic(Duration(seconds: 2), (_) async {
setState(() {
if (_currentScrollIndex == widget.newsList.length - 1) {
_timer.cancel();
_selectedNews = null;
} else {
_selectedNews = widget.newsList[++_currentScrollIndex];
_animateToIndex(_currentScrollIndex);
}
});
});
}
We don't need the scroll direction defined as _autoScrollIncrement. However, I would introduce a new _selectedNews to easily unselect the last News item when we arrive at the bottom of the list. The selected flag of our ListTile would then become:
#override
Widget build(BuildContext context) {
return ListView(
[...]
children: widget.newsList
.map((news) => ListTile(
[...]
selected: _selectedNews?.id == news.id,
[...]
))
.toList(),
);
}

Creating pageview with dynamic number of pages in flutter

Please find the code I have created. The problem here I face is the third item is getting called thrise when I increment the item count of the page view. Please let me know if there is any fix for this. I think this happens due to setState rebuild the widget. If I comment the setState it will work but dynamic page creation doesn't work. Let me know the fix for this.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../sub_screens/single_article_item.dart';
import '../sub_screens/two_article_item.dart';
import '../sub_screens/three_article_item.dart';
class ArticleScreen extends StatefulWidget {
#override
_ArticleScreenState createState() => _ArticleScreenState();
}
class _ArticleScreenState extends State<ArticleScreen> {
var homeDataList = List<Home>();
int _pageNo = 3;
void setPageNo(int page) {
setState(() {
_pageNo = page;
});
//notifyListeners();
}
#override
Widget build(BuildContext context) {
final vm = Provider.of<HomeViewModel>(context, listen: false);
homeDataList.add(Home("First Article Screen"));
homeDataList.add(Home("Second Article Screen"));
homeDataList.add(Home("Other type Article Screen"));
PageController controller = PageController(
keepPage: false,
initialPage: 0,
);
var currentPageValue = 0.0;
// return ChangeNotifierProvider(create: (ctx)=> LocationProvider(),child:MaterialApp(
// home: Scaffold(body: Container(child: LocationScreen(),)),
// ));
// }
// }
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: Container(
child: PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if (position == 0) {
//vm.fetchHomeData(":::third page");
print('first article');
return SingleArticleItem();
} else if (position == 1) {
print('two article');
return TwoArticleItem();
} else {
print("third article " +
_pageNo.toString() +
"::" +
position.toString());
return ThreeArticleItem(vm.getPageNo, vm.getHomeData);
}
},
onPageChanged: (index) {
if (index >= 1) {
// print('onPageChanged - two article');
controller.addListener(() {
setState(() {
currentPageValue = controller.page;
_pageNo = _pageNo + 1;
// print('onPageChanged - three article');
});
});
}
},
// itemCount: vm.getPageNo,
itemCount: vm.getPageNo,
scrollDirection: Axis.vertical,
),
),
),
),
);
}
}

Is there a better way of async loading data into CupertinoPicker?

Is there a better way of async on-demand loading data into CupertinoPicker?
What I basically do is listen to scroll event and detecting if CupertinoPicker is scrolled beyond the start and show a spinner for the duration of the loading of "data". I insert the spinner into the list of widgets built from the data and remove it when new widgets are added to the list.
I can't say what makes me wonder if there is a better way, but I feel there should be a better way of solving this issue.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Widget> items = [Divider(color: Colors.red)];
bool refreshInProgress = false;
loadMoreData() {
if(refreshInProgress) return;
setState(() {
refreshInProgress = true;
});
items.insert(0, FutureBuilder(
future: Future.delayed(Duration(seconds: 2)).then((value) {
var newItems = [Text("A"), Text("B"), Text("C")];
setState((){
items = [...newItems, ...items.where((w) => !(w is FutureBuilder) || w == null)];
refreshInProgress = false;
});
return newItems;
}),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.waiting) return Center(child: CircularProgressIndicator());
return null;
},
));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(child: Container(
margin: EdgeInsets.all(20),
child: Column(children: [
Expanded(flex: 4, child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if ((notification is ScrollEndNotification) && notification.metrics.pixels <= 0) {
loadMoreData();
return true;
}
return false;
},
child: CupertinoPicker.builder(
itemExtent: 32,
itemBuilder: (BuildContext context, int index) {
if(index >= 0 && index < items.length) return Center(child: items[index]);
return null;
},
onSelectedItemChanged: (int value) {
print("onSelectedItemChanged: $value");
},
))
),
Expanded(child: Container(child: Text("$items"))),
Row(children: [
RaisedButton(onPressed: () => setState(() => loadMoreData()), child: Text("Load more data")),
])
],
)),
)),
);
}
}
https://dartpad.dev/b27137f4bff39281980f5957f5e140ad

Best practices for implementing an infinite scroll GridView in Flutter?

I want to create a GridView that displays items which will be fetching from server by offset. I load only 10 items in GridView, and after user scroll reached to 10, I will load more 10 items. What are the best practices for implementing an infinite scroll GridView in Flutter?
You need to add the ScrollController for the scrolling detection at the bottom for the ListView and GridView. As you need the GridView i have created the ScrollController listner and added to the GridView's contollerfor the detection of the scroll. I have created the demo of it , please check it once. At first time it load the 10 items and when list comes to the bottom then it add more 10 items in it.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeState();
}
}
class HomeState extends State<HomeScreen> {
List dataList = new List<int>();
bool isLoading = false;
int pageCount = 1;
ScrollController _scrollController;
#override
void initState() {
super.initState();
////LOADING FIRST DATA
addItemIntoLisT(1);
_scrollController = new ScrollController(initialScrollOffset: 5.0)
..addListener(_scrollListener);
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gridview',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.red,
accentColor: Color(0xFFFEF9EB),
),
home: Scaffold(
appBar: new AppBar(),
body: GridView.count(
controller: _scrollController,
scrollDirection: Axis.vertical,
crossAxisCount: 2,
mainAxisSpacing: 10.0,
physics: const AlwaysScrollableScrollPhysics(),
children: dataList.map((value) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.2,
margin: EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Text("Item ${value}"),
);
}).toList(),
)));
}
//// ADDING THE SCROLL LISTINER
_scrollListener() {
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
print("comes to bottom $isLoading");
isLoading = true;
if (isLoading) {
print("RUNNING LOAD MORE");
pageCount = pageCount + 1;
addItemIntoLisT(pageCount);
}
});
}
}
////ADDING DATA INTO ARRAYLIST
void addItemIntoLisT(var pageCount) {
for (int i = (pageCount * 10) - 10; i < pageCount * 10; i++) {
dataList.add(i);
isLoading = false;
}
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}
And output of the above program as follow
class AllOrdersPage extends StatefulWidget {
#override
_AllOrdersPageState createState() => _AllOrdersPageState();
}
class _AllOrdersPageState extends State<AllOrdersPage> {
List<OrderDatum> ordersList;
ScrollController _scrollController = ScrollController();
int skip = 0;
bool shouldLoadMore = true;
Future<OrdersResponse> future;
#override
void initState() {
super.initState();
ordersList = [];
future = getAllOrders(skip); //load data for first time
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) { //Check whether user scrolled to last position
if (shouldLoadMore) {
setState(() {
skip += ordersList.length;
future = getAllOrders(skip); //load more data
});
}
}
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<OrdersResponse>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasError)
return ErrorText('${snapshot.error.toString()}');
if (snapshot.hasData) {
skip = snapshot.data.skip;
if (snapshot.data.limit + snapshot.data.skip >=
snapshot.data.total) {
shouldLoadMore = false;
}
snapshot.data.data.forEach((element) {
if (!ordersList.contains(element)) ordersList.add(element);
});
if (skip == 0 && ordersList.isEmpty) {
return ErrorText('No orders.');
}
return Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
childAspectRatio: 2.5,
mainAxisSpacing: 8),
controller: _scrollController,
padding: const EdgeInsets.all(8),
itemBuilder: (BuildContext context, int index) {
if (index == ordersList.length) {
return shouldLoadMore
? Center(child: CircularProgressIndicator())
: Container();
}
return Container(
width: MediaQuery.of(context).size.width,
child: OrderCard(ordersList[index]));
},
itemCount: ordersList.length + 1,
),
);
}
return Loader();
});
}
}
Thanks