Wrap stacked cards in flutter - flutter

I'm trying to stack some playing cards like the image shows. BUT I want the cards to wrap into a new line when there is no more space on the device.
This is what I have so far
class CollectionOfCards extends StatelessWidget {
List<PlayingCard> cards;
CollectionOfCards(this.cards);
#override
Widget build(BuildContext context) {
return Stack(
children: [
...cards
.asMap()
.map(
(key, value) => MapEntry(
key,
Positioned(
child: PlayingCardWidget(value),
left: 35.0 * key,
),
),
)
.values
.toList(),
],
overflow: Overflow.visible,
);
}
}
Any ideas how to do this? I imagine it is somewhat a common task.
Another idea would be to make a fan of cards that can always fit but I imagine thats harder to achieve!

Assume we want to have 2 rows of cards, each containing 10 cards. We need to use Positioned in a way that every card is positioned according to its index in the row(from 0 to 9) and its column(column 0 or column 1). So we need to set top and left in the Positioned widget. The 0.7 * cardHeight in top is for the vertical stacking, you can change that to cardHeight if you don't want to have vertical stacking.
Note: Set width and height of the cards using MediaQuery size. I didn't have the cards so I just made empty containers instead.
class _MyHomePageState extends State<MyHomePage> {
List<Widget> _cards = [];
void _initCards() {
double cardHeight = 400 / 2;
double cardWidth = 2500 / 20;
for (int i = 0; i < 20; i++) {
_cards.add(Positioned(
left: ((i >= 10) ? i - 10 : i) * cardWidth / 2,
top: (i >= 10) ? 0.7 * cardHeight : 0,
child: Card(
elevation: 10,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Container(width: cardWidth, height: cardHeight),
),
));
}
}
#override
void initState() {
super.initState();
_initCards();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(horizontal: 15.0),
child: Stack(
children: _cards
),
decoration: BoxDecoration(
color: Colors.yellow,
),
),
);
}
}
Result:

Just for reference. I ended up with solution like this inspired by #Mobina's answer
class CollectionOfCards extends StatelessWidget {
final List<PlayingCard> cards;
final double stackSkew = 0.4;
CollectionOfCards(this.cards);
#override
Widget build(BuildContext context) {
final double widgetWidth = MediaQuery.of(context).size.width - 16;
final double cardWith = PlayingCardWidget.defaultWidth(context);
final int cardsPerRow =
(widgetWidth / cardWith / stackSkew - 1 / stackSkew).floor();
return Stack(
children: [
...cards
.asMap()
.map(
(index, card) {
return MapEntry(
index,
Positioned(
child: Draggable(
childWhenDragging: Container(),
feedback: PlayingCardWidget(card: card),
child: PlayingCardWidget(
card: card,
),
),
left: stackSkew *
cardWith *
(index - (cardsPerRow) * (index / cardsPerRow).floor()),
top: (card.heightFromWidth(cardWith) + 2) *
(index / cardsPerRow).floor(),
),
);
},
)
.values
.toList(),
],
overflow: Overflow.visible,
);
}
}
It seems to work flawless.

One way you can do this is by passing left as well as top parameter to Positioned calculated based on -
Screen width
Card index ("key" in your code)
Card width
Card height
See the demo on dartpad - https://dartpad.dev/d36e018c1f1c7a6cd4b91d5b09e69a7c

Try using Wrap() widget inside a Column()
Column(
children: <Widget>[
Wrap(
children: <Widget>[
//your card widget
]
)
]
)

Related

How to animate multiple widgets while scrolling in Flutter

I need to implement custom animation while scrolling the list of users. See an example
My current view is composed of next elements:
SingleChildScrollView contains Column with:
Row of three top elements (each of is a custom widget with basically Stack of avatar, medal and details (Column))
Row as a table header
ListView of other users.
SingleChildScrollView is wrapped with NotificationListener for ScrollNotification which is populated to provider. The scroll value is then listened in every top element to perform animation of its own.
I would like to know some general path and algorithm here to take. I tried AnimatedPositioned but as soon as it is applied on multiple elements it causes performance issues. Should I use AnimationController or some more custom things so far? Any help would be appreciated.
As pskink mentioned, using SliverPersistentHeader can be archive, This is a demo widget to illustrate how it can be done. You need to play with value. My favorite part is using .lerp , doubleLerp... to position the items.
class Appx extends StatelessWidget {
const Appx({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(),
),
const SliverToBoxAdapter(
child: SizedBox(
height: 3333,
width: 200,
),
),
],
),
);
}
}
class CustomSliverPersistentHeaderDelegate
extends SliverPersistentHeaderDelegate {
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return LayoutBuilder(builder: (_, constraints) {
final t = shrinkOffset / maxExtent;
final width = constraints.maxWidth;
final itemMaxWidth = width / 4;
double xFactor = -.4;
return ColoredBox(
color: Colors.cyanAccent.withOpacity(.3),
child: Stack(
children: [
Align(
alignment:
Alignment.lerp(Alignment.center, Alignment(xFactor, -.2), t)!
..x,
child: buildRow(
color: Colors.deepPurple, itemMaxWidth: itemMaxWidth, t: t),
),
Align(
alignment: Alignment.lerp(
Alignment.centerRight, Alignment(xFactor, 0), t)!,
child:
buildRow(color: Colors.red, itemMaxWidth: itemMaxWidth, t: t),
),
Align(
alignment: Alignment.lerp(
Alignment.centerLeft, Alignment(xFactor, .2), t)!,
child: buildRow(
color: Colors.amber, itemMaxWidth: itemMaxWidth, t: t),
),
],
),
);
});
}
Container buildRow(
{required Color color, required double itemMaxWidth, required double t}) {
return Container(
width: lerpDouble(itemMaxWidth, itemMaxWidth * .3, t),
height: lerpDouble(itemMaxWidth, itemMaxWidth * .3, t),
color: color,
);
}
/// you need to increase when it it not pinned
#override
double get maxExtent => 400;
#override
double get minExtent => 300;
#override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}

Flutter transform rotate inside row / column

I have a Column with some widgets inside.
If I trying to rotate one of them, it behaves like in the stack widget.
But I want to save layout of the rotated widget inside parent widget.
My question is how to revert (return) layout of the rotated widget inside Column, Row, etc... ?
The explanation and code example:
Right layout without rotation:
right layout screenshot
Column(
children: [
Text('Progress'),
SizedBox(height: 8.0,),
LinearProgressIndicator(
value: _counter / 100,
minHeight: 20,
),
],
)
Wrong layout with rotated widget:
wrong layout screenshot
Column(
children: [
Text('Progress'),
SizedBox(height: 8.0,),
Transform.rotate(
angle: -45 / (180 / pi),
child: LinearProgressIndicator(
value: _counter / 100,
minHeight: 20,
),
),
],
)
Wanted result screenshot
Thanks to Yeasin Sheikh for good idea,
task solved, final code here
Transform widget is just painting on UI, doesn't take actual space. You can wrap with SizedBox to provide the height.
If you can tweak the flexibility of your own from
deg2Ran(double deg) => deg / (180 / pi);
double boxHeight() {
final value = (((500 * deg2Ran(angle))) / (deg2Ran(-45))).abs();
return value < 20 ? 20.0 : value; // 20 your min height
}
class SX extends StatefulWidget {
const SX({Key? key}) : super(key: key);
#override
State<SX> createState() => _SXState();
}
class _SXState extends State<SX> {
double angle = -45;
deg2Ran(double deg) => deg / (180 / pi);
double boxHeight() {
final value = (((500 * deg2Ran(angle))) / (deg2Ran(-45))).abs();
return value < 20 ? 20.0 : value; // 20 your min height
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slider(
min: -180,
max: 180,
value: angle,
onChanged: (v) {
setState(() {
angle = v;
});
}),
Text('Progress'),
SizedBox(
height: 8.0,
),
SizedBox(
height: boxHeight(),
child: Center(
child: Transform.rotate(
angle: deg2Ran(angle),
alignment: Alignment.center,
child: LinearProgressIndicator(
value: 22 / 100,
minHeight: 20,
),
),
),
),
],
),
);
}
}
You can handle overflow and some decoration

Flutter putting Image.network into Dart isolate

Here i have a simple class that i show images from network with Image.network. as i used this class into a Listview, during scrolling that cause of a bit lag and i think i can fix it with Isolate.
after reading some documentation about this feature in Dart i'm not sure how can i put this this class or part of that such as a simple widget into that.
class InistaLikers extends HookWidget {
final List<String> imageUrls;
const InistaLikers({required this.imageUrls});
#override
Widget build(BuildContext context) {
late double _width = 0;
late int count = 4;
final _orientation = MediaQuery.of(context).orientation;
final _screenWidth = MediaQuery.of(context).size.width;
useEffect((){
if(_orientation == Orientation.portrait){
_width = _screenWidth* 0.39;
count = 4;
}else if(_orientation == Orientation.landscape){
_width = (_screenWidth / 2) * 0.39;
count = 3;
}
});
return Container(
width: _width,
height: 35.0,
child: Row(
children: [
Expanded(
child: Stack(
children: List.generate(
count,
(i) {
return Positioned(
right: imageUrls.length + (20.0 * i),
child: ClipOval(
child: Container(
width: 35,
height: 35,
child: Image.network(
imageUrls[i],
),
),
),
);
},
).toList(),
),
),
ClipOval(
child: Container(
width: 35,
height: 35,
child: Image.network(
imageUrls.last,
),
),
),
],
),
);
}
}
You cannot build a widget through an isolate as dart:ui which is used to render your widgets is only available on the main isolate. Moreover, Image.network already uses an ImageStream to manage the recuperation of an online image.
If you have some performance issues you should try to optimize the way you are building your widgets, for example if it was not the case already you should use ListView.builder if you have a lot of widgets to render.
You can find some "Performance best practices" documentation on the flutter website or the article Flutter Performance Tips written by Hasan Basri Bayat.
Here's some of the tips described in this article which you can apply to improve the performances of your app:
Use Widgets Over Functions
// Don't do this
[
_buildHeaderWidget(),
_buildMainWidget(context),
_buildFooterWidget(),
]
// Do this
[
HeaderWidget(),
MainWidget(),
FooterWidget(),
]
Use const where possible
const _myFixedHeight = 48.0;
Use const constructors whenever possible
class CustomWidget extends StatelessWidget {
const CustomWidget();
#override
Widget build(BuildContext context) {
// ...
}
}
Use nil instead of Container()
// Don't do this
Column(
children: [
text != null ? Text(text) : Container(),
],
)
// Do this
Column(
children: [
if (text != null)
Text(text),
],
)
And you'll find some more tips in the article.

if statement in ListView flutter

I'm trying to have a responsive drawer that when the screen size is higher than X itemExtent is set to default, how could I add a if statement inside a ListView?
Widget drawer(context){
double _containerHeight = MediaQuery. of(context). size. height;
int _numberOfListTiles = 16;
return new Drawer(
child: Container(
height: _containerHeight,
color: const Color(0xff68778d),
child: ListView(
padding: EdgeInsets.zero,
itemExtent: _containerHeight/_numberOfListTiles,
children: [
],
),
)
);
}
I would like to do:
if(_containerHeight < 700){
itemExtent: _containerHeight/_numberOfListTiles,
}
The cleanest way (in my opinion) would be to instantiate your variable at the top of your drawer method as you did for the _containerHieght and _numberOfListTiles variables.
Your code would then look like that
Widget drawer(context) {
final double _containerHeight = MediaQuery.of(context).size.height;
final int _numberOfListTiles = 16;
double itemExtent;
if(_containerHeight < 700){
itemExtent = _containerHeight/_numberOfListTiles;
} else {
itemExtent = 100; // Default ?
}
return new Drawer(
child: Container(
height: _containerHeight,
color: const Color(0xff68778d),
child: ListView(
padding: EdgeInsets.zero,
itemExtent: _containerHeight / _numberOfListTiles,
children: [],
),
));
}

Is there any way to get this kind of functionality in Flutter? Sticker View for Flutter?

I need to implement of such feature where Text or Any widget can be resized by user using a corner icons. I'm attaching a reference. Any help would be great.
I want to implement One Finger Rotate using the Icon.
I've tried implementing some similar features of few Android Sticker View library but I am not getting proper result.
I've tried the Following code.
import 'package:flutter/material.dart';
import 'package:matrix4_transform/matrix4_transform.dart';
import 'dart:math';
import 'package:render_metrics/render_metrics.dart';
class DragRotate extends StatefulWidget {
DragRotate({Key key}) : super(key: key);
#override
_DragRotateState createState() => _DragRotateState();
}
class _DragRotateState extends State<DragRotate> {
Matrix4 matrixForObject = Matrix4.identity();
double centerx, centery;
RenderParametersManager renderManager = RenderParametersManager();
GlobalKey sdsd = new GlobalKey();
#override
Widget build(BuildContext context) {
return Container(
child: Container(
child: Stack(
children: [
Center(
child: Transform(
origin: Offset(100, 100),
transform: matrixForObject,
child: Container(
key: sdsd,
height: 200,
width: 200,
color: Colors.amber,
child: Stack(
children: [
RenderMetricsObject(
manager: renderManager,
id: "w1",
child: Container(
margin: EdgeInsets.all(10),
color: Colors.amberAccent,
),
),
Positioned(
top: 0,
right: 0,
child: GestureDetector(
onPanStart: (p) {
//// THIS IS USED TO GET THE CENTER X CORDINATE OF THE ROTATING OBJECT
centerx =
renderManager.getRenderData("w1").center.x;
//// THIS IS USED TO GET THE CENTER Y CORDINATE OF THE ROTATING OBJECT
centery =
renderManager.getRenderData("w1").center.y;
},
onPanUpdate: (s) {
double angel = atan2(
s.globalPosition.dy - centery,
s.globalPosition.dx - centerx) *
180 /
pi;
setState(() {
matrixForObject = Matrix4Transform()
.rotateByCenterDegrees(angel, Size(1, 1))
.matrix4;
});
},
child: Icon(Icons.rotate_90_degrees_ccw))),
],
),
),
),
),
],
),
),
);
}
}