Flutter Draggable ListView feedback scroll position - flutter

While using Flutter, I am trying to put a ListView inside of a Draggable. Using PageStorageKey I was able to preserve the scroll position before and after dragging with this minimal example:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget list = ListView(
key: const PageStorageKey("id"),
controller: ScrollController(),
children: const [
Text("1"),
Text("2"),
Text("3"),
Text("4"),
Text("5"),
Text("6"),
Text("7"),
Text("8"),
Text("9"),
Text("10"),
Text("11")
],
);
double x = 300;
double y = 100;
#override
Widget build(BuildContext context) {
final listWidget = SizedBox(
width: x,
height: y,
child: Card(
elevation: 0,
shape: const RoundedRectangleBorder(
side: BorderSide(color: Colors.black),
borderRadius: BorderRadius.all(Radius.circular(12))),
child: list,
));
return Scaffold(
body: Draggable(
child: listWidget,
feedback: listWidget,
childWhenDragging: const SizedBox()));
}
}
However, once drag starts the feedback widget does not have the same scroll position. Is there a way to preserve the scroll position during drag?

Related

Flutter) I want to change screen in ModalBottomSheet... WHAT IS PROBLEM..?

I made an int variable tab1 and a function addTab to control the screen in modalBottomSheet, and I thought that when TextButton in HandAdd1() is pressed, a function addTab is executed, and changes the int variable tab1.
In MyApp(), as a result HandAdd2() widget is shown according to the list in MyApp(), but int variable tab1 doesn't change no matter how many times I press the TextButton in HandAdd1(), so the screen doesn't change, and I don't know what the problem is.
Can you help me ?
Here's the code:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int tab1 = 0;
addTab (){
setState(() {
tab1++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton:FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
padding:EdgeInsets.fromLTRB(20,30,20,20),
height:MediaQuery.of(context).size.height * 50,
margin: const EdgeInsets.only(
left: 0,
right: 0,
bottom: 0,
),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft:Radius.circular(25),
topRight: Radius.circular(25),
),
),
child: Scaffold(body:[HandAdd1(addTab:addTab) , HandAdd2()][tab1]),
);
},
backgroundColor: Colors.transparent,
);
);
}
}
and here's HandAdd1() WIdget. (HandAdd2() is almost similar to HandAdd1())
class HandAdd1 extends StatefulWidget {
const HandAdd1({Key? key, this.addTab}) : super(key: key);
final void addTab;
#override
HandAdd1State createState() => HandAdd1State();
}
class HandAdd1State extends State<HandAdd1> {
#override
Widget build(BuildContext context) {
return Column(
children:[
Align(alignment:Alignment.centerLeft, child:Padding(
padding:EdgeInsets.fromLTRB(0,20,0,0,),
child:Text('''title''' ,
),
),
),
Padding(
padding:EdgeInsets.fromLTRB(0,20,0,20,),
child:Container(),
),
InkWell(child: Container(
margin:EdgeInsets.fromLTRB(0,20,0,0,),
alignment:Alignment.center,
width: MediaQuery.of(context).size.width * 50,
height:60,
decoration: BoxDecoration(
borderRadius:BorderRadius.all(Radius.circular(10)),
color: Colors.pinkAccent,
),
child:Text('button' , style: TextStyle(color:Colors.white , fontWeight:FontWeight.w500 , fontSize: 20,))
),
onTap: (){
widget.addTab;
}
),
],
);
}
}
You need to pass addTab function to a addTab property as a VoidCallback.
In your HandAdd1 widget,
Change from final void addTab; to final VoidCallback addTab;
And from widget.addTab; to widget.addTab();

Flutter: Custom widget with PageView physics and interactions

I need to make a screen on which the interaction is similar to PageView, but with something very different on the screen. I don't want to deal with separate pages. I only need the current scroll position to place my widgets as I desire.
I succeeded in achieving this by placing PageView with empty pages at the bottom of the Stack and placing my widgets inside AnimatedBuilder above PageView. The only problem is that my widgets have their own GestureDetectors and that's why PageView doesn't scroll when I start scrolling gestures over my widget.
Here is a sample. I need the same behavior but without scroll problems when I start dragging over my top widgets.
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _pageController = PageController(viewportFraction: 0.7, initialPage: 5);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
PageView.builder(
controller: _pageController,
itemBuilder: (context, index) => Container(),
itemCount: 10,
),
for (double angleOffset in [0.25, 0.5, 0.75, 1])
AnimatedBuilder(
animation: _pageController,
builder: (context, child) {
return Center(
child: Transform.rotate(
angle: (_pageController.page ?? 5) + pi * angleOffset,
child: Transform.translate(
offset: const Offset(0, 150),
child: const ColorContainer(),
),
),
);
},
),
],
),
);
}
}
class ColorContainer extends StatefulWidget {
const ColorContainer({Key? key}) : super(key: key);
#override
State<ColorContainer> createState() => _ColorContainerState();
}
class _ColorContainerState extends State<ColorContainer> {
double hue = 30;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => hue = (hue + 80.0) % 360),
child: Container(
width: 100,
height: 100,
decoration: ShapeDecoration(
shape: const CircleBorder(),
color: HSLColor.fromAHSL(1, hue, 1, 0.5).toColor(),
),
),
);
}
}

PageView is causing unbounded height error

class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void openModal() {
showModalBottomSheet<dynamic>(
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20))),
context: context,
builder: (_) {
return Wrap(
children: [
ModalSheetContent(),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FloatingActionButton(
onPressed: openModal,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
);
}
}
class ModalSheetContent extends StatefulWidget {
const ModalSheetContent({Key? key}) : super(key: key);
#override
_ModalSheetContentState createState() => _ModalSheetContentState();
}
class _ModalSheetContentState extends State<ModalSheetContent> {
final PageController pageController = PageController(
initialPage: 0,
keepPage: true,
);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: PageView(
controller: pageController,
children: [imageVideo(context), Pricing()],
),
);
}
I want the modalSheet to take the dynamic height according to its content and not full screen. I tried wrapping PageView with Column and Expanded but it didn't work.
Error:
Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand.The relevant error-causing widget was PageView
PageView must have a height in flutter, usually it takes the biggest part of the screen but in your case you are showing it inside a bottomsheet, I assume that your bottomsheet should be almost full screen, so try to give it a height
final size = MediaQuery.of(context).size;
……
SizedBox(
height: size.height - 100;
child: PageView(…),
)
If you want that your bottom sheet takes dynamic height according to its context, simple wrap your widget into column widget and add this attribute.
Column(
mainAxisSize:MainAxisSize.min,
children:[...]
)

How to constraint item position in flutter?

I try to make a list of widget. It look like this:
I know of no such thing as Constraint Layout in flutter. But I need something to position my arrow icon in a fixed position on the right. To put it simple, this is my widget code:
Row(
children:[
SizedBox(),
Column(),//this is all the item on the left
Spacer(),
Expanded(// this is the heart and arrow button
child: Column()
)
]
)
I notice that if my column on the left get too wide, my arrow and heart icon is shifted out of line.
How to put my icon in fixed position to the right?
here try this, You have to wrap middle column with expanded so it will take the maximum space available
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (_, index) => Container(
padding: EdgeInsets.all(15),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),border: Border.all(width: 1.5)),
child: Row(
children: [
Container(
width: 25,
height: 40,
color: Colors.black,
),
Expanded(
child: Column(children: [
//put your children here
]),
),
//this will be always on right
Column(
children: [
Icon(Icons.heart_broken),
Icon(Icons.chevron_right),
],
)
],
),
),
);
}
}

Clip Stack children

How to clip Stack children within it's size.
In this image there are 3 grid-Items using orange color and every item using InkWell to use hover-Method to Align on Stack. While hover:false the Pop PoP Widget won't be visible to the UI. With align property it works, but as you can see the Right Top GridItem's item:2 pop POp widget is visible outside the Stack<Griditem> and I want to make it invisible outside the stack. I've tested using clipBehavior: with every Clip enums.
I want to hide the Pop POp widget while it is outside the Stack and yes I need this pop-up effect.
For Flutter web and I'm using Flutter V2.5.2
Current Layout with Issue
Full Code to reproduce the issue
import 'package:flutter/material.dart';
void main() => runApp(
const MaterialApp(
home: Appp(),
),
);
class Appp extends StatelessWidget {
const Appp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const BodyX();
}
}
class BodyX extends StatelessWidget {
const BodyX({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
return GridView.count(
crossAxisCount: 2,
children: [
...List.generate(
3,
(index) => GridItem(
key: UniqueKey(),
maxWidth: constraints.maxWidth / 2,
),
),
],
);
},
));
}
}
class GridItem extends StatefulWidget {
const GridItem({
Key? key,
required this.maxWidth,
}) : super(key: key);
final double maxWidth;
#override
State<GridItem> createState() => _AppXState();
}
class _AppXState extends State<GridItem> {
bool _isHovered = false;
#override
Widget build(BuildContext context) {
print("ItemWidth : ${widget.maxWidth}");
return SizedBox(
//though it wont effect here,
// just finding the size of Grid because it will 1x1
width: widget.maxWidth,
height: widget.maxWidth,
child: InkWell(
onTap: () {},
hoverColor: Colors.black,
onHover: (value) {
setState(() {
_isHovered = value;
});
},
child: Stack(
clipBehavior: Clip.antiAliasWithSaveLayer,
children: [
Container(
color: Colors.deepOrange.withOpacity(.2),
),
AnimatedAlign(
alignment: Alignment(0, _isHovered ? .7 : 2),
child: Container(
padding: const EdgeInsets.all(22),
color: Colors.greenAccent,
child: const Text(
"Pop POp",
),
),
duration: const Duration(
milliseconds: 200,
),
)
],
),
),
);
}
}
If you don't want a Widget to draw beyond its layout size, you can use ClipRect to clip it.
In your case, you can wrap ClipRect on your Stack, like so:
ClipRect(
child: Stack(
children: ...
),
)
Further more, you can use ClipRRect to clip a rounded rectangle shape (circular border) or ClipPath to clip a custom shape, like a triangle. You can read more about these widgets in the official docs.