flutter-reorderable-grid-view
flutter-reorderable-grid-view copied to clipboard
Can pageview be supported?
hi, thank you for helping me with this project,but the pageview cannot work well, do you have plans to deal with this in the future.
flutter_reorderable_grid_view: ^5.0.0-dev.2
flutter:3.7.6
demo:https://github.com/JDongKhan/quick_desktop.git
https://user-images.githubusercontent.com/4929805/222666434-939ea635-6b20-4069-b5e4-5ec15ed0cd09.mov
Widget _buildPageView() {
return ReorderableBuilder(
key: Key(_gridViewKey.toString()),
scrollController: _pageController,
onReorder: _handleReorder,
builder: (children) {
return PageView(
key: _gridViewKey,
controller: _pageController,
scrollDirection: Axis.vertical,
children: [
GridView(),
GridView(),
],
);
},
children: _getGeneratedChildren(),
);
}
Hello @JDongKhan
I never thought using it that way! :D
I don't know if its a complicated feature or not. That depends on PageView
. But usually I only made this feature for one GridView
. In your video it seems like it works half of it.
Did you try to add ReorderableBuilder
to your GridView
, so that you use 2 of them? I don't know if that could already work
Thanks for response.I want to make an application similar to apple desktop and try to add ReorderableBuilder to my GridView, unfortunately it didn't meet my expectations.
This is my core code
Widget _buildPageView() {
return ReorderableBuilder(
key: Key(_gridViewKey.toString()),
scrollController: _pageController,
onReorder: _handleReorder,
builder: (children) {
return GridOrPageView(
key: _gridViewKey,
controller: _pageController,
minChildWidth: 100,
minChildHeight: 100,
pagePadding: const EdgeInsets.all(20),
indicatorColor: Colors.grey,
scrollDirection: Axis.vertical,
indicatorSelectorColor: Colors.white,
children: children,
// overlapIndicator: false,
);
},
children: dataList.map((e) => _buildItem(e)).toList(),
);
}
Widget _buildItem(String e) {
return Container(
key: Key(e),
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
border: Border.all(color: Colors.white, width: 1),
borderRadius: BorderRadius.circular(10),
),
alignment: Alignment.center,
child: Text(
e,
style: const TextStyle(color: Colors.black),
),
);
}
import 'package:flutter/material.dart';
import 'package:page_indicator/page_indicator.dart';
class GridOrPageView extends StatelessWidget {
final PageController controller;
final List<Widget> children;
final bool showIndicator;
final bool overlapIndicator;
final int minChildWidth;
final int minChildHeight;
final ValueChanged<int>? onPageChanged;
final ValueChanged<int>? onPageAmountChanged;
final EdgeInsets indicatorPadding;
final Color indicatorColor;
final Color indicatorSelectorColor;
late final EdgeInsets pagePadding;
final Axis scrollDirection;
GridOrPageView({
super.key,
required this.controller,
required this.children,
required this.minChildWidth,
required this.minChildHeight,
this.scrollDirection = Axis.horizontal,
this.onPageChanged,
this.onPageAmountChanged,
this.showIndicator = true,
this.overlapIndicator = true,
this.indicatorPadding = const EdgeInsets.only(bottom: 10.0, top: 10.0),
this.indicatorSelectorColor = Colors.blue,
this.indicatorColor = Colors.grey,
pagePadding = EdgeInsets.zero,
}) {
this.pagePadding = overlapIndicator
? pagePadding
: pagePadding.add(const EdgeInsets.only(bottom: 32));
}
@override
Widget build(BuildContext context) {
if (children.isEmpty) return Container();
return LayoutBuilder(builder: (context, constraints) {
int pages = (children.length / _itemsPerPage(constraints)).ceil();
onPageAmountChanged?.call(pages);
if (showIndicator && pages > 1) {
return PageIndicatorContainer(
align: IndicatorAlign.bottom,
padding: indicatorPadding,
length: pages,
indicatorColor: indicatorColor,
indicatorSelectorColor: indicatorSelectorColor,
child:
_pageViewBuilder(pages, _itemsPerPage(constraints), constraints),
);
} else {
return _pageViewBuilder(pages, _itemsPerPage(constraints), constraints);
}
});
}
Widget _pageViewBuilder(
int pages, int itemsPerPage, BoxConstraints constraints) {
return PageView.builder(
scrollDirection: scrollDirection,
controller: controller,
onPageChanged: onPageChanged,
itemCount: pages,
itemBuilder: (context, index) {
int start = index * itemsPerPage;
int end = start + itemsPerPage;
if (end >= this.children.length) end = this.children.length;
List<Widget> children = this.children.sublist(start, end);
return Padding(
padding: pagePadding,
child: KeepAliveGridView(
childAspectRatio: _calcAspectRatio(constraints),
crossAxisCount: _calcColumns(constraints),
children: children,
),
);
},
);
}
int _calcRows(BoxConstraints constraints) {
int rows = (_availableHeight(constraints) / minChildHeight).floor();
return (rows < 1)
? 1
: (_availableHeight(constraints) / minChildHeight).floor();
}
int _calcColumns(BoxConstraints constraints) {
int columns = (_availableWidth(constraints) / minChildWidth).floor();
return (columns < 1)
? 1
: (_availableWidth(constraints) / minChildWidth).floor();
}
double _calcAspectRatio(BoxConstraints constraints) {
double width = _availableWidth(constraints) / _calcColumns(constraints);
double height = _availableHeight(constraints) / _calcRows(constraints);
return width / height;
}
int _itemsPerPage(BoxConstraints constraints) {
int itemsPerPage = _calcRows(constraints) * _calcColumns(constraints);
if (itemsPerPage >= children.length) itemsPerPage = children.length;
return itemsPerPage;
}
double _availableWidth(BoxConstraints constraints) =>
constraints.maxWidth - pagePadding.left - pagePadding.right;
double _availableHeight(BoxConstraints constraints) =>
constraints.maxHeight - pagePadding.bottom - pagePadding.top;
}
class KeepAliveGridView extends StatefulWidget {
final List<Widget> children;
final int crossAxisCount;
final double childAspectRatio;
const KeepAliveGridView({
super.key,
required this.children,
required this.childAspectRatio,
required this.crossAxisCount,
});
@override
State<KeepAliveGridView> createState() => _KeepAliveGridViewState();
}
class _KeepAliveGridViewState extends State<KeepAliveGridView>
with AutomaticKeepAliveClientMixin {
final _gridViewKey = GlobalKey();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return GridView(
key: _gridViewKey,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.crossAxisCount,
childAspectRatio: widget.childAspectRatio,
),
children: widget.children,
);
}
@override
bool get wantKeepAlive => true;
}
https://user-images.githubusercontent.com/4929805/222872124-7825ffa5-1694-4a8b-b67a-4725d5c3eb43.mov
https://user-images.githubusercontent.com/4929805/222872127-33ba2245-e95a-4d5b-aec0-ca1061ef664b.mov
_scrollPositionPixels is incorrect when _checkToScrollWhileDragging multiple executions.
first: _scrollPositionPixels = 2013 is correct second: _scrollPositionPixels = 1807 is incorrect
https://user-images.githubusercontent.com/4929805/222879401-43264649-af6d-4780-a3b1-932ee5b02341.mov
log: Dragged:[[<'79'>]]: Original OrderId: 77, Updated OrderId: 90, Original Offset: Offset(953.4, 660.2), Updated Offset: Offset(538.5, 816.0) - updatedCollisionEntity[[<'93'>]]: Original OrderId: 91, Updated OrderId: 91, Original Offset: Offset(642.2, 816.0), Updated Offset: Offset(642.2, 816.0)
79 should have exchanged places with 124, but the 93 be found.
Hmm Ok, I will check that, maybe it's a problem that is not too hard to solve, not sure @JDongKhan
Can you build an example code for me that I could replace with the main.dart
code in the example app? Then it's easier for me to reproduce this issue and to fix it
Ok,This is my example.
flutter_reorderable_grid_view: ^5.0.0-dev.2
https://github.com/JDongKhan/flutter_reorderable_grid_view_demo.git
or
import 'package:flutter/material.dart';
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _gridViewKey = GlobalKey();
final PageController _pageController = PageController();
late List<String> dataList;
@override
void initState() {
dataList = List.generate(200, (index) => '$index');
super.initState();
}
Widget _buildPageView() {
return LayoutBuilder(builder: (context, cs) {
double cellSize = 100;
int column = cs.maxWidth ~/ cellSize;
int row = cs.maxHeight ~/ cellSize;
int itemsPerPage = column * row;
int page = (dataList.length / itemsPerPage).ceil();
return ReorderableBuilder(
key: Key(_gridViewKey.toString()),
scrollController: _pageController,
onReorder: _handleReorder,
builder: (children) {
return PageView(
key: _gridViewKey,
controller: _pageController,
scrollDirection: Axis.vertical,
children: List.generate(
page,
(index) {
int start = index * itemsPerPage;
int end = start + itemsPerPage;
if (end >= children.length) end = children.length;
return Padding(
padding: const EdgeInsets.all(8.0),
child: GridView(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: column,
childAspectRatio: 1,
),
children: children.sublist(start, end),
),
);
},
),
// overlapIndicator: false,
);
},
children: _getGeneratedChildren(),
);
});
}
void _handleReorder(ReorderedListFunction reorderedListFunction) {
setState(() {
dataList = reorderedListFunction(dataList) as List<String>;
});
}
List<Widget> _getGeneratedChildren() {
return dataList.map((e) => _buildItem(e)).toList();
}
Widget _buildItem(String e) {
return Container(
key: Key(e),
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
border: Border.all(color: Colors.white, width: 1),
borderRadius: BorderRadius.circular(10),
),
alignment: Alignment.center,
child: Text(
e,
style: const TextStyle(color: Colors.black),
),
);
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
backgroundColor: Colors.green,
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: _buildPageView(),
);
}
}
Thank you very much, I will try to check out this in the next days, this week is a little bit busy so, probably I can test it on the weekend @JDongKhan
Thank you very much, I will try to check out this in the next days, this week is a little bit busy so, probably I can test it on the weekend @JDongKhan
ok
I tried out your example and I think it won't be that easy to support the drag and drop through multiple GridViews. My code was only designed to support the drag and drop for just one GridView.
Because your wished feature could take a lot more time before I could support this, I will let that issue as enhancement. But I don't think it will come soon because I am still working on the update for version 5.0.0
.
Is it very important that the drag and drop works through multiple GridViews? Because if it is only important that the drag and drop works on every page of yourPageView
, you could try to build a code that splits your items in sublists and these sublists are added to every GridView that has an own ReorderableBuilder
. Then you would just reorder your sublist and not the whole list. @JDongKhan