Skip to content

Instantly share code, notes, and snippets.

@followthemoney1
Created August 26, 2021 22:07
Show Gist options
  • Save followthemoney1/257e61588786241b0634a74ffc6dcc2e to your computer and use it in GitHub Desktop.
Save followthemoney1/257e61588786241b0634a74ffc6dcc2e to your computer and use it in GitHub Desktop.
codepen_example
import 'package:flutter/material.dart';
import 'dart:developer';
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'dart:html' as html;
import 'dart:ui' as ui;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final itemSize = 60.0;
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: AnimatedCategory<String>(
childBuilder: (item, String data) {
return YoutubePlayer();
},
startSize: itemSize,
deltaSizeFirstTap: itemSize / 2,
deltaSizeSecondTap: itemSize * 1.8,
columnNumber: 2,
items: [
'',
'',
'',
'',
''
],
itemSelected: (SuggestionItem i) {},
),
),
),
);
}
}
class YoutubePlayer extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return YoutubePlayerState();
}
}
class YoutubePlayerState extends State<YoutubePlayer> {
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (BuildContext ctx, BoxConstraints constraints) {
Size playerSize = Size(
constraints.maxWidth,
constraints.maxHeight,
);
return playerSize.width <= 0
? Container()
: Container(
width: playerSize.width,
height: playerSize.height,
child:IframeScreen(),
);
});
}
@override
void dispose() {
super.dispose();
}
}
class IframeScreen extends StatefulWidget {
@override
_IframeScreenState createState() => _IframeScreenState();
}
class _IframeScreenState extends State<IframeScreen> {
late Widget _iframeWidget;
final html.IFrameElement _iframeElement = html.IFrameElement();
@override
void initState() {
super.initState();
_iframeElement.height = '50';
_iframeElement.width = '50';
_iframeElement.src = 'https://www.youtube.com/embed/liEGSeD3Zt8';
_iframeElement.style.border = 'none';
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
_iframeWidget = HtmlElementView(
key: UniqueKey(),
viewType: 'iframeElement',
);
}
@override
Widget build(BuildContext context) {
//
final _width = 500.0;
final _height = 500.0;
return Center(
child: SizedBox(
height: _height,
width: _width,
child: _iframeWidget,
),
)
;
}
@override
void dispose() {
super.dispose();
}
}
class AnimatedCategory<T> extends StatefulWidget {
final List items;
final Function itemSelected;
final double startSize;
final double deltaSizeFirstTap;
final double deltaSizeSecondTap;
final Widget Function(SuggestionItem item, T data) childBuilder;
final bool setClickedItemDelay;
final int clickedItemDelay;
final int itemAnimationDuration;
final Curve itemCurve;
final int stackAnimatedDuration;
final Curve stackCurve;
final Key key;
final int columnNumber;
const AnimatedCategory(
{Key this.key = const ValueKey(101010),
required this.childBuilder,
required this.items,
required this.itemSelected,
this.setClickedItemDelay = false,
this.clickedItemDelay = 100,
this.startSize = 100.0,
this.deltaSizeSecondTap = 200.0,
this.deltaSizeFirstTap = 50.0,
this.itemAnimationDuration = 400,
this.itemCurve = Curves.bounceInOut,
this.stackAnimatedDuration = 600,
this.columnNumber = 4,
this.stackCurve = Curves.easeInOutQuint})
: super(key: key);
@override
_AnimatedCategoryState createState() => _AnimatedCategoryState<T>(childBuilder);
}
class _AnimatedCategoryState<T> extends State<AnimatedCategory> with TickerProviderStateMixin {
Widget Function(SuggestionItem item, T data) childBuilder;
_AnimatedCategoryState(this.childBuilder);
Map<int, List<SuggestionItem>> suggestionMatrix = {};
var needUpdateScrollWidth = true;
var maxWidth = 0;
late var startSize;
late var deltaSize;
late var deltaSizeBig;
@override
void initState() {
super.initState();
startSize = widget.startSize;
deltaSize = widget.deltaSizeFirstTap;
deltaSizeBig = widget.deltaSizeSecondTap;
int rowCount = (widget.items.length / widget.columnNumber).round();
for (int m = 0; m < widget.columnNumber; m++) {
suggestionMatrix.addAll({m: []});
print(suggestionMatrix.length);
}
suggestionMatrix = Map.from(suggestionMatrix.map((key, value) {
final endIndex = (rowCount * (key + 1));
return MapEntry(
key,
widget.items
.getRange(rowCount * key, endIndex < widget.items.length ? endIndex : widget.items.length)
.toList()
.asMap()
.entries
.map((element) {
final val = element.value;
final i = element.key;
return SuggestionItem(
data: val,
width: startSize,
height: startSize,
currentWeight: 1,
x: (i) * startSize as double,
y: (key) * startSize as double,
);
}).toList());
}));
}
@override
Widget build(BuildContext context) {
updateWidth();
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: maxWidth * startSize as double?,
height: 4 * deltaSizeBig as double?,
child: Stack(
fit: StackFit.expand,
clipBehavior: Clip.hardEdge,
alignment: Alignment.topCenter,
children: childerCards(),
)),
);
}
updateWidth() {
if (needUpdateScrollWidth) {
needUpdateScrollWidth = false;
// maxWidth = 0;
suggestionMatrix.forEach((key, list) {
final current = list.where((element) => element.currentWeight == 1).length;
final currentExpand = list.where((element) => element.currentWeight == 2).length;
final currentExpandMax = list.where((element) => element.currentWeight == 3).length;
final all = current + (currentExpand * 2) + (currentExpandMax * 2);
maxWidth = maxWidth < all ? all : maxWidth;
});
}
}
List<Widget> childerCards() {
List<Widget> cardsMatrixWidgets = [];
suggestionMatrix.entries.forEach((columns) {
int iColumn = columns.key;
List<SuggestionItem> rowsList = columns.value;
rowsList.asMap().entries.forEach((rows) {
int iRow = rows.key;
SuggestionItem currentRow = rows.value;
setState(() {
currentRow.iColumn = iColumn;
currentRow.iRow = iRow;
});
///mark: update widgets
///
if (widget.setClickedItemDelay) {
Future.delayed(Duration(milliseconds: widget.clickedItemDelay), () {
_update(currentRow: currentRow, rowsList: rowsList);
});
} else {
_update(currentRow: currentRow, rowsList: rowsList);
}
///get all widgets
///
///
cardsMatrixWidgets.add(AnimatedPositioned.fromRect(
duration: Duration(milliseconds: widget.stackAnimatedDuration),
curve: widget.stackCurve, //fastOutSlowIn,
child: item(rows.value),
rect: currentRow.rect,
));
});
});
return cardsMatrixWidgets;
}
_update({required final currentRow, final rowsList}) {
if (currentRow.iRow > 0) {
calcOverflowLeft(rowsList.elementAt(currentRow.iRow - 1), currentRow);
}
setState(() {
if (currentRow.iColumn > 0) {
calcOverflowTop(suggestionMatrix[currentRow.iColumn - 1]!.elementAt(currentRow.iRow), currentRow);
calcOverflowClosestElement(line: suggestionMatrix[currentRow.iColumn - 1]!, current: currentRow);
}
});
}
bool calcOverflowClosestElement(
{required List<SuggestionItem> line, required SuggestionItem current, bool check = false}) {
for (SuggestionItem element in line) {
if (current.rect.intersect(element.rect).height > 0 && current.rect.intersect(element.rect).width > 0) {
if (current.rect.intersect(element.rect).height > 0) {
if (!check) {
current.y += element.rect.intersect(current.rect).height;
}
}
}
}
return false;
}
void calcOverflowLeft(SuggestionItem prev, SuggestionItem current, {bool? withGravity}) {
if (prev.right > current.left) {
current.x += prev.right - current.left;
} else if (prev.right < current.left) {
current.x -= current.left - prev.right;
}
}
void calcOverflowTop(SuggestionItem prev, SuggestionItem current) {
if (current.x == prev.x) current.y += prev.rect.intersect(current.rect).height;
}
Widget item(SuggestionItem e) {
return AnimatedContainer(
duration: Duration(milliseconds: widget.itemAnimationDuration),
curve: widget.itemCurve,
height: e.height,
width: e.width,
child: Padding(padding: EdgeInsets.all(8), child: childBuilder(e, e.data)).addOnTap(
onLongPress: () {
onLongPressItem(e);
setState(() {
needUpdateScrollWidth = true;
});
},
onTap: () {
onTapItem(e);
setState(() {
needUpdateScrollWidth = true;
});
},
),
);
}
onTapItem(SuggestionItem e) {
setState(() {
if (e.currentWeight <= 1) {
e.currentWeight = e.currentWeight + 1;
e.height = e.height + deltaSize;
e.width = e.width + deltaSize;
} else {
e.currentWeight = 1;
e.height = startSize;
e.width = startSize;
}
});
widget.itemSelected(e);
}
onLongPressItem(
SuggestionItem e,
) {
setState(() {
if (e.currentWeight < 3) {
e.currentWeight = 3;
e.height = deltaSizeBig;
e.width = deltaSizeBig;
} else {
e.currentWeight = 1;
e.height = startSize;
e.width = startSize;
}
});
widget.itemSelected(e);
}
}
class SuggestionItem<T> {
final T data;
int currentWeight;
double width;
double height;
double x;
double y;
int? iRow;
int? iColumn;
get selected => currentWeight > 1;
get superLike => currentWeight == 3;
get leftTop => x + y;
get rightTop => y + x + width;
get leftBottom => x + y + height;
get rightBottom => leftTop + width + height;
get left => x;
get right => x + width;
get bottom => y + height;
get top => y;
get isExpanded => currentWeight > 1;
get color => Colors.black;
Rect get rect => Rect.fromLTWH(left, top, width, height);
SuggestionItem({
required this.data,
required this.width,
required this.height,
this.currentWeight = 1,
required this.x,
required this.y,
});
}
class CardItemWidget extends StatefulWidget {
final Widget child;
final Color? backgroundColor;
final IconData? iconData;
final int weight;
final double? width;
final double? height;
final String? name;
final SuggestionItem? el;
final String? localImage;
const CardItemWidget(
{this.backgroundColor,
this.iconData,
this.weight = 1,
this.width,
this.height,
this.name,
this.localImage,
required this.child,
this.el});
@override
State<StatefulWidget> createState() {
return CardItemWidgetState();
}
}
class CardItemWidgetState extends State<CardItemWidget>
with TickerProviderStateMixin {
final Shader linearGradient = LinearGradient(
colors: <Color>[Color(0xFF9B51E0), Color(0xff5D3DF3)],
).createShader(Rect.fromLTWH(0.0, 0.0, 100.0, 70.0));
@override
Widget build(BuildContext context) {
final unselectedStyle = Theme.of(context).textTheme.headline5!.copyWith(
color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold);
final selectedStyle = Theme.of(context).textTheme.headline5!.copyWith(
foreground: (Paint()..shader = linearGradient),
fontSize: 18,
fontWeight: FontWeight.bold);
final selected = widget.el!.selected;
final superLike = widget.el!.superLike;
return widget.child;
}
}
//MARK: widget ext
extension WidgetExtension on Widget {
Widget expand() {
return Expanded(child: this);
}
Widget paddingAll(double value) {
return Padding(
padding: EdgeInsets.all(value),
child: this,
);
}
Widget paddingOnly({double left = 0, double right = 0, double top = 0, double bottom = 0}) {
return Container(
color: Colors.transparent,
child: Padding(
padding: EdgeInsets.only(
left: left != 0 ? left : 0,
right: right != 0 ? right : 0,
top: top != 0 ? top : 0,
bottom: bottom != 0 ? bottom : 0,
),
child: this,
),
);
}
Widget addOnTap({required Function()? onTap, Function()? onLongPress}) {
return GestureDetector(
child: Container(child: this),
onTap: onTap,
onLongPress: onLongPress,
);
}
Widget insideScroll() {
return SingleChildScrollView(child: this);
}
Widget insideScrollAndExpand() {
return Expanded(child: SingleChildScrollView(child: this));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment