chat module auth flow completed
11
assets/icons/facebook.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_36951_1908)">
|
||||
<path d="M12 22.125C17.5919 22.125 22.125 17.5919 22.125 12C22.125 6.40812 17.5919 1.875 12 1.875C6.40812 1.875 1.875 6.40812 1.875 12C1.875 17.5919 6.40812 22.125 12 22.125Z" fill="black"/>
|
||||
<path d="M6.4042 6.375L11.0373 12.58L6.375 17.625H7.42437L11.5063 13.2079L14.8042 17.625H18.375L13.4812 11.071L17.8208 6.375H16.7714L13.0124 10.4429L9.975 6.375H6.4042ZM7.94734 7.14916H9.58775L16.8317 16.8508H15.1912L7.94734 7.14916Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_36951_1908">
|
||||
<rect width="21" height="21" fill="white" transform="translate(1.5 1.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 723 B |
6
assets/icons/google.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.7498 12.2233C21.7498 11.5605 21.695 10.8942 21.5781 10.2422H12.1963V13.9966H17.5688C17.3459 15.2075 16.6295 16.2786 15.5806 16.9593V19.3954H18.7858C20.668 17.6973 21.7498 15.1896 21.7498 12.2233Z" fill="#4285F4"/>
|
||||
<path d="M12.196 21.7506C14.8786 21.7506 17.1409 20.8872 18.7892 19.3969L15.584 16.9608C14.6922 17.5555 13.541 17.8922 12.1997 17.8922C9.60478 17.8922 7.40461 16.1762 6.61518 13.8691H3.30762V16.3804C4.99612 19.6727 8.43525 21.7506 12.196 21.7506Z" fill="#34A853"/>
|
||||
<path d="M6.61196 13.8692C6.19532 12.6584 6.19532 11.3472 6.61196 10.1363V7.625H3.30805C1.89732 10.3799 1.89732 13.6256 3.30805 16.3805L6.61196 13.8692Z" fill="#FBBC04"/>
|
||||
<path d="M12.196 6.10897C13.6141 6.08747 14.9846 6.61051 16.0116 7.57061L18.8513 4.78704C17.0532 3.13194 14.6666 2.222 12.196 2.25066C8.43525 2.25066 4.99612 4.32848 3.30762 7.62435L6.61153 10.1357C7.3973 7.82497 9.60112 6.10897 12.196 6.10897Z" fill="#EA4335"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/icons/qrr.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
assets/icons/s1.png
Normal file
After Width: | Height: | Size: 691 KiB |
BIN
assets/icons/s2.png
Normal file
After Width: | Height: | Size: 1003 KiB |
BIN
assets/icons/s3.png
Normal file
After Width: | Height: | Size: 510 KiB |
BIN
assets/icons/s4.png
Normal file
After Width: | Height: | Size: 975 KiB |
BIN
assets/icons/s5.png
Normal file
After Width: | Height: | Size: 495 KiB |
692
assets/icons/slider_test.dart
Normal file
@@ -0,0 +1,692 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OnBoardingSlider extends StatefulWidget {
|
||||
const OnBoardingSlider({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<OnBoardingSlider> createState() => _OnBoardingSliderState();
|
||||
}
|
||||
|
||||
class _OnBoardingSliderState extends State<OnBoardingSlider> {
|
||||
final PageController _pageController = PageController();
|
||||
int _currentPage = 0;
|
||||
|
||||
@override
|
||||
var imageList = [
|
||||
'assets/s1.png',
|
||||
'assets/s2.png',
|
||||
'assets/s3.png',
|
||||
'assets/s4.png',
|
||||
'assets/s5.png'
|
||||
];
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
Widget buildDot(int index) {
|
||||
return Container(
|
||||
margin: EdgeInsets.all(5),
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: currentIndex == index ? Colors.white : Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGallery3D() {
|
||||
return Gallery3D(
|
||||
controller: Gallery3DController(
|
||||
itemCount: imageList.length,
|
||||
ellipseHeight: 0,
|
||||
autoLoop: true,
|
||||
minScale: 0.4,
|
||||
delayTime: 2000,
|
||||
scrollTime: 1000,
|
||||
),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 300,
|
||||
isClip: true,
|
||||
onItemChanged: (index) {
|
||||
setState(
|
||||
() {
|
||||
currentIndex = index;
|
||||
_currentPage = index;
|
||||
},
|
||||
);
|
||||
},
|
||||
itemConfig: const GalleryItemConfig(
|
||||
width: 180,
|
||||
height: 850,
|
||||
radius: 10,
|
||||
isShowTransformMask: false,
|
||||
),
|
||||
onClickItem: (index) {
|
||||
if (kDebugMode) print("currentIndex:$index");
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
return Image.asset(
|
||||
imageList[index],
|
||||
fit: BoxFit.fill,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var screenHeight = MediaQuery.of(context).size.height;
|
||||
|
||||
return Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
// Expanded(
|
||||
// child:
|
||||
Container(
|
||||
height: screenHeight / 4,
|
||||
child: PageView.builder(
|
||||
controller: _pageController,
|
||||
itemCount: imageList.length,
|
||||
itemBuilder: ((context, index) {
|
||||
return buildGallery3D();
|
||||
}),
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
_currentPage = index;
|
||||
});
|
||||
},
|
||||
),
|
||||
// ),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
imageList.length,
|
||||
(index) => buildDot(index),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Gallery3D extends StatefulWidget {
|
||||
final double? height;
|
||||
final double width;
|
||||
final IndexedWidgetBuilder itemBuilder;
|
||||
final ValueChanged<int>? onItemChanged;
|
||||
final ValueChanged<int>? onClickItem;
|
||||
final Gallery3DController controller;
|
||||
final GalleryItemConfig itemConfig;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final bool isClip;
|
||||
|
||||
Gallery3D(
|
||||
{Key? key,
|
||||
this.onClickItem,
|
||||
this.onItemChanged,
|
||||
this.isClip = true,
|
||||
this.height,
|
||||
this.padding,
|
||||
required this.itemConfig,
|
||||
required this.controller,
|
||||
required this.width,
|
||||
required this.itemBuilder})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_Gallery3DState createState() => _Gallery3DState();
|
||||
}
|
||||
|
||||
class _Gallery3DState extends State<Gallery3D>
|
||||
with TickerProviderStateMixin, WidgetsBindingObserver, Gallery3DMixin {
|
||||
List<Widget> _galleryItemWidgetList = [];
|
||||
AnimationController? _autoScrollAnimationController;
|
||||
Timer? _timer;
|
||||
|
||||
late Gallery3DController controller = widget.controller;
|
||||
|
||||
AppLifecycleState appLifecycleState = AppLifecycleState.resumed;
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
appLifecycleState = state;
|
||||
super.didChangeAppLifecycleState(state);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
controller.widgetWidth = widget.width;
|
||||
controller.vsync = this;
|
||||
controller.init(widget.itemConfig);
|
||||
|
||||
_updateWidgetIndexOnStack();
|
||||
if (controller.autoLoop) {
|
||||
this._timer =
|
||||
Timer.periodic(Duration(milliseconds: controller.delayTime), (timer) {
|
||||
if (!mounted) return;
|
||||
if (appLifecycleState != AppLifecycleState.resumed) return;
|
||||
if (DateTime.now().millisecondsSinceEpoch - _lastTouchMillisecond <
|
||||
controller.delayTime) return;
|
||||
if (_isTouching) return;
|
||||
animateTo(controller.getOffsetAngleFormTargetIndex(
|
||||
getNextIndex(controller.currentIndex)));
|
||||
});
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
_autoScrollAnimationController?.stop(canceled: true);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void animateTo(angle) {
|
||||
_isTouching = true;
|
||||
_lastTouchMillisecond = DateTime.now().millisecondsSinceEpoch;
|
||||
_scrollToAngle(angle);
|
||||
}
|
||||
|
||||
@override
|
||||
void jumpTo(angle) {
|
||||
setState(() {
|
||||
_updateAllGalleryItemTransformByAngle(angle);
|
||||
});
|
||||
}
|
||||
|
||||
var _isTouching = false;
|
||||
var _lastTouchMillisecond = 0;
|
||||
Offset? _panDownLocation;
|
||||
Offset? _lastUpdateLocation;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width,
|
||||
height: widget.height ?? widget.itemConfig.height,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onHorizontalDragCancel: (() {
|
||||
_onFingerUp();
|
||||
}),
|
||||
onHorizontalDragDown: (details) {
|
||||
_isTouching = true;
|
||||
_panDownLocation = details.localPosition;
|
||||
_lastUpdateLocation = details.localPosition;
|
||||
_lastTouchMillisecond = DateTime.now().millisecondsSinceEpoch;
|
||||
},
|
||||
onHorizontalDragEnd: (details) {
|
||||
_onFingerUp();
|
||||
},
|
||||
onHorizontalDragStart: (details) {},
|
||||
onHorizontalDragUpdate: (details) {
|
||||
setState(() {
|
||||
_lastUpdateLocation = details.localPosition;
|
||||
_lastTouchMillisecond = DateTime.now().millisecondsSinceEpoch;
|
||||
_updateAllGalleryItemTransformByOffsetDx(details.delta.dx);
|
||||
});
|
||||
},
|
||||
child: _buildWidgetList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWidgetList() {
|
||||
if (widget.isClip) {
|
||||
return ClipRect(
|
||||
child: Stack(
|
||||
children: _galleryItemWidgetList,
|
||||
));
|
||||
}
|
||||
return Stack(
|
||||
children: _galleryItemWidgetList,
|
||||
);
|
||||
}
|
||||
|
||||
void _scrollToAngle(double angle) {
|
||||
_autoScrollAnimationController =
|
||||
AnimationController(duration: Duration(milliseconds: 100), vsync: this);
|
||||
|
||||
Animation animation;
|
||||
|
||||
if (angle.ceil().abs() == 0) return;
|
||||
animation =
|
||||
Tween(begin: 0.0, end: angle).animate(_autoScrollAnimationController!);
|
||||
|
||||
double lastValue = 0;
|
||||
animation.addListener(() {
|
||||
setState(() {
|
||||
_updateAllGalleryItemTransformByAngle(animation.value - lastValue);
|
||||
lastValue = animation.value;
|
||||
});
|
||||
});
|
||||
_autoScrollAnimationController?.forward();
|
||||
_autoScrollAnimationController?.addListener(() {
|
||||
if (_autoScrollAnimationController != null &&
|
||||
_autoScrollAnimationController!.isCompleted) {
|
||||
_isTouching = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onFingerUp() {
|
||||
if (_lastUpdateLocation == null) {
|
||||
_isTouching = false;
|
||||
return;
|
||||
}
|
||||
|
||||
double angle = controller.getTransformInfo(controller.currentIndex).angle;
|
||||
double targetAngle = 0;
|
||||
|
||||
var offsetX = _lastUpdateLocation!.dx - _panDownLocation!.dx;
|
||||
|
||||
// Adjust the threshold for considering it a swipe
|
||||
double swipeThreshold = widget.width * 0.1;
|
||||
|
||||
if (offsetX.abs() > swipeThreshold) {
|
||||
// Swipe detected
|
||||
if (offsetX > 0) {
|
||||
// Swipe to the right, move to the bottom
|
||||
targetAngle = controller
|
||||
.getTransformInfo(getPreIndex(controller.currentIndex))
|
||||
.angle +
|
||||
180; // Move to the bottom
|
||||
} else {
|
||||
// Swipe to the left
|
||||
targetAngle = angle + 180; // Maintain the current position
|
||||
}
|
||||
} else {
|
||||
// No swipe, move to the bottom from the current position
|
||||
targetAngle = angle + 180; // Move to the bottom
|
||||
}
|
||||
|
||||
_scrollToAngle(targetAngle);
|
||||
}
|
||||
|
||||
void _updateAllGalleryItemTransformByAngle(double angle) {
|
||||
controller.updateTransformByAngle(angle);
|
||||
_updateAllGalleryItemTransform();
|
||||
}
|
||||
|
||||
void _updateAllGalleryItemTransformByOffsetDx(double offsetDx) {
|
||||
controller.updateTransformByOffsetDx(offsetDx);
|
||||
_updateAllGalleryItemTransform();
|
||||
}
|
||||
|
||||
void _updateAllGalleryItemTransform() {
|
||||
for (var i = 0; i < controller.getTransformInfoListSize(); i++) {
|
||||
var item = controller.getTransformInfo(i);
|
||||
|
||||
if (item.angle > 180 - controller.unitAngle / 2 &&
|
||||
item.angle < 180 + controller.unitAngle / 2) {
|
||||
if (controller.currentIndex != i) {
|
||||
controller.currentIndex = i;
|
||||
widget.onItemChanged?.call(controller.currentIndex);
|
||||
}
|
||||
}
|
||||
_updateWidgetIndexOnStack();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get Pre Index >>>
|
||||
int getPreIndex(int index) {
|
||||
var preIndex = index - 1;
|
||||
if (preIndex < 0) {
|
||||
preIndex = controller.itemCount - 1;
|
||||
}
|
||||
return preIndex;
|
||||
}
|
||||
|
||||
/// Get Next Index >>>
|
||||
int getNextIndex(int index) {
|
||||
var nextIndex = index + 1;
|
||||
if (nextIndex == controller.itemCount) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
return nextIndex;
|
||||
}
|
||||
|
||||
List<GalleryItem> _leftWidgetList = [];
|
||||
List<GalleryItem> _rightWidgetList = [];
|
||||
List<GalleryItem> _tempList = [];
|
||||
|
||||
/// Update Widget Index >>>
|
||||
void _updateWidgetIndexOnStack() {
|
||||
_leftWidgetList.clear();
|
||||
_rightWidgetList.clear();
|
||||
_tempList.clear();
|
||||
|
||||
for (var i = 0; i < controller.getTransformInfoListSize(); i++) {
|
||||
var angle = controller.getTransformInfo(i).angle;
|
||||
|
||||
if (angle >= 180 + controller.unitAngle / 2) {
|
||||
_leftWidgetList.add(_buildGalleryItem(
|
||||
i,
|
||||
));
|
||||
} else {
|
||||
_rightWidgetList.add(_buildGalleryItem(
|
||||
i,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
_rightWidgetList.sort((widget1, widget2) =>
|
||||
widget1.transformInfo.angle.compareTo(widget2.transformInfo.angle));
|
||||
|
||||
_rightWidgetList.forEach((element) {
|
||||
if (element.transformInfo.angle < controller.unitAngle / 2) {
|
||||
element.transformInfo.angle += 360;
|
||||
_tempList.add(element);
|
||||
}
|
||||
});
|
||||
|
||||
_tempList.forEach((element) {
|
||||
_rightWidgetList.remove(element);
|
||||
});
|
||||
|
||||
_leftWidgetList.sort((widget1, widget2) =>
|
||||
widget2.transformInfo.angle.compareTo(widget1.transformInfo.angle));
|
||||
|
||||
_galleryItemWidgetList = [
|
||||
..._leftWidgetList,
|
||||
..._rightWidgetList,
|
||||
];
|
||||
}
|
||||
|
||||
/// Build Gallery Item >>>>>
|
||||
GalleryItem _buildGalleryItem(int index) {
|
||||
return GalleryItem(
|
||||
index: index,
|
||||
ellipseHeight: controller.ellipseHeight,
|
||||
builder: widget.itemBuilder,
|
||||
config: widget.itemConfig,
|
||||
onClick: (index) {
|
||||
if (widget.onClickItem != null && index == controller.currentIndex) {
|
||||
widget.onClickItem?.call(index);
|
||||
}
|
||||
},
|
||||
transformInfo: controller.getTransformInfo(index),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GalleryItemTransformInfo {
|
||||
Offset offset;
|
||||
double scale;
|
||||
double angle;
|
||||
int index;
|
||||
|
||||
_GalleryItemTransformInfo(
|
||||
{required this.index,
|
||||
this.scale = 1,
|
||||
this.angle = 0,
|
||||
this.offset = Offset.zero});
|
||||
}
|
||||
|
||||
class GalleryItem extends StatelessWidget {
|
||||
final GalleryItemConfig config;
|
||||
final double ellipseHeight;
|
||||
final int index;
|
||||
final IndexedWidgetBuilder builder;
|
||||
final ValueChanged<int>? onClick;
|
||||
final _GalleryItemTransformInfo transformInfo;
|
||||
|
||||
final double minScale;
|
||||
GalleryItem({
|
||||
Key? key,
|
||||
required this.index,
|
||||
required this.transformInfo,
|
||||
required this.config,
|
||||
required this.builder,
|
||||
this.minScale = 0.4,
|
||||
this.onClick,
|
||||
this.ellipseHeight = 0,
|
||||
}) : super(key: key);
|
||||
|
||||
Widget _buildItem(BuildContext context) {
|
||||
return Container(
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
child: builder(context, index));
|
||||
}
|
||||
|
||||
Widget _buildMaskTransformItem(Widget child) {
|
||||
if (!config.isShowTransformMask) return child;
|
||||
return Stack(children: [
|
||||
child,
|
||||
Container(
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
color: Color.fromARGB(
|
||||
100 * (1 - transformInfo.scale) ~/ (1 - minScale), 0, 0, 0),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _buildRadiusItem(Widget child) {
|
||||
if (config.radius <= 0) return child;
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(config.radius), child: child);
|
||||
}
|
||||
|
||||
Widget _buildShadowItem(Widget child) {
|
||||
if (config.shadows.isEmpty) return child;
|
||||
return Container(
|
||||
child: child, decoration: BoxDecoration(boxShadow: config.shadows));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Transform.translate(
|
||||
offset: transformInfo.offset,
|
||||
child: Container(
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
child: Transform.scale(
|
||||
scale: transformInfo.scale,
|
||||
child: InkWell(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
onTap: () => onClick?.call(index),
|
||||
child: _buildShadowItem(
|
||||
_buildRadiusItem(_buildMaskTransformItem(_buildItem(context)))),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gallery Item Config >>>>>
|
||||
class GalleryItemConfig {
|
||||
final double width;
|
||||
final double height;
|
||||
final double radius;
|
||||
final List<BoxShadow> shadows;
|
||||
final bool isShowTransformMask;
|
||||
|
||||
const GalleryItemConfig(
|
||||
{this.width = 220,
|
||||
this.height = 600,
|
||||
this.radius = 0,
|
||||
this.isShowTransformMask = true,
|
||||
this.shadows = const []});
|
||||
}
|
||||
|
||||
class Gallery3DController {
|
||||
double perimeter = 0;
|
||||
double unitAngle = 0;
|
||||
final double minScale;
|
||||
double widgetWidth = 0;
|
||||
double ellipseHeight;
|
||||
int itemCount;
|
||||
late GalleryItemConfig itemConfig;
|
||||
int currentIndex = 0;
|
||||
final int delayTime;
|
||||
final int scrollTime;
|
||||
final bool autoLoop;
|
||||
late Gallery3DMixin vsync;
|
||||
List<_GalleryItemTransformInfo> _galleryItemTransformInfoList = [];
|
||||
double baseAngleOffset = 0;
|
||||
Gallery3DController(
|
||||
{required this.itemCount,
|
||||
this.ellipseHeight = 0,
|
||||
this.autoLoop = true,
|
||||
this.minScale = 0.4,
|
||||
this.delayTime = 5000,
|
||||
this.scrollTime = 1000})
|
||||
: assert(itemCount >= 3, 'ItemCount must be greater than or equal to 3');
|
||||
|
||||
void init(GalleryItemConfig itemConfig) {
|
||||
this.itemConfig = itemConfig;
|
||||
unitAngle = 360 / itemCount;
|
||||
perimeter = calculatePerimeter(widgetWidth * 0.7, 50);
|
||||
|
||||
_galleryItemTransformInfoList.clear();
|
||||
for (var i = 0; i < itemCount; i++) {
|
||||
var itemAngle = getItemAngle(i);
|
||||
_galleryItemTransformInfoList.add(_GalleryItemTransformInfo(
|
||||
index: i,
|
||||
angle: itemAngle,
|
||||
scale: calculateScale(itemAngle),
|
||||
offset: calculateOffset(itemAngle)));
|
||||
}
|
||||
}
|
||||
|
||||
_GalleryItemTransformInfo getTransformInfo(int index) {
|
||||
return _galleryItemTransformInfoList[index];
|
||||
}
|
||||
|
||||
int getTransformInfoListSize() {
|
||||
return _galleryItemTransformInfoList.length;
|
||||
}
|
||||
|
||||
double getItemAngle(int index) {
|
||||
double angle = 360 - (index * unitAngle + 180) % 360;
|
||||
return angle;
|
||||
}
|
||||
|
||||
void updateTransformByAngle(double offsetAngle) {
|
||||
baseAngleOffset -= offsetAngle;
|
||||
for (int index = 0; index < _galleryItemTransformInfoList.length; index++) {
|
||||
_GalleryItemTransformInfo transformInfo =
|
||||
_galleryItemTransformInfoList[index];
|
||||
|
||||
double angle = getItemAngle(index);
|
||||
double scale = transformInfo.scale;
|
||||
Offset offset = transformInfo.offset;
|
||||
|
||||
if (baseAngleOffset.abs() > 360) {
|
||||
baseAngleOffset %= 360;
|
||||
}
|
||||
|
||||
angle += baseAngleOffset;
|
||||
angle = angle % 360;
|
||||
|
||||
offset = calculateOffset(angle);
|
||||
|
||||
scale = calculateScale(angle);
|
||||
|
||||
transformInfo
|
||||
..angle = angle
|
||||
..scale = scale
|
||||
..offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
void updateTransformByOffsetDx(double offsetDx) {
|
||||
double offsetAngle = offsetDx / perimeter / 2 * 360;
|
||||
updateTransformByAngle(offsetAngle);
|
||||
}
|
||||
|
||||
/// Calculate Scale >>>>
|
||||
double calculateScale(double angle) {
|
||||
angle = angle % 360;
|
||||
if (angle > 180) {
|
||||
angle = 360 - angle;
|
||||
}
|
||||
|
||||
angle += 30;
|
||||
|
||||
var scale = angle / 180.0;
|
||||
|
||||
if (scale > 1) {
|
||||
scale = 1;
|
||||
} else if (scale < minScale) {
|
||||
scale = minScale;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
/// Calculate Offset >>>>
|
||||
Offset calculateOffset(double angle) {
|
||||
double width = widgetWidth * 1;
|
||||
|
||||
double radiusOuterX = width / 2.4;
|
||||
|
||||
double radiusOuterY = ellipseHeight;
|
||||
|
||||
double angleOuter = (4 * pi / 360) * angle;
|
||||
double x = radiusOuterX * sin(angleOuter);
|
||||
double y = radiusOuterY > 0 ? radiusOuterY * cos(angleOuter) : 0;
|
||||
|
||||
return Offset(x + (widgetWidth - itemConfig.width) / 2, -y);
|
||||
}
|
||||
|
||||
/// Calculate Perimeter >>>>
|
||||
double calculatePerimeter(double width, double height) {
|
||||
var a = width;
|
||||
// var a = width * 0.8;
|
||||
var b = height;
|
||||
return 2 * pi * b + 4 * (a - b);
|
||||
}
|
||||
|
||||
/// Get Final Angle >>>>
|
||||
double getFinalAngle(double angle) {
|
||||
if (angle >= 360) {
|
||||
angle -= 360;
|
||||
} else if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
double getOffsetAngleFormTargetIndex(int index) {
|
||||
double targetItemAngle = getItemAngle(index) + baseAngleOffset;
|
||||
|
||||
double offsetAngle = targetItemAngle % 180;
|
||||
if (targetItemAngle < 180 || targetItemAngle > 360) {
|
||||
offsetAngle = offsetAngle - 180;
|
||||
}
|
||||
|
||||
return offsetAngle;
|
||||
}
|
||||
|
||||
void animateTo(int index) {
|
||||
if (index == currentIndex) return;
|
||||
vsync.animateTo(getOffsetAngleFormTargetIndex(index));
|
||||
}
|
||||
|
||||
void jumpTo(int index) {
|
||||
if (index == currentIndex) return;
|
||||
vsync.jumpTo(getOffsetAngleFormTargetIndex(index));
|
||||
}
|
||||
}
|
||||
|
||||
mixin class Gallery3DMixin {
|
||||
void animateTo(angle) {}
|
||||
void jumpTo(angle) {}
|
||||
}
|
11
assets/icons/x.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_36951_2023)">
|
||||
<circle cx="12" cy="12" r="10.5" fill="#1878F3"/>
|
||||
<path d="M13.5771 5.25006C14.3958 5.24821 15.214 5.2883 16.0283 5.37018V8.13971H14.3398C13.018 8.13971 12.7598 8.74822 12.7598 9.64557V11.6202H15.9189L15.5088 14.7217H12.7598V22.4688C12.5087 22.4868 12.2556 22.5001 12 22.5001C11.1351 22.5001 10.2953 22.3926 9.49121 22.1954V14.7217H6.75V11.6202H9.49121V9.33795C9.49124 6.69177 11.1478 5.25006 13.5771 5.25006Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_36951_2023">
|
||||
<rect width="21" height="21" fill="white" transform="translate(1.5 1.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 708 B |
BIN
ios/assets/fonts/SF Pro/SFProDisplay-Semibold.ttf
Normal file
BIN
ios/assets/fonts/SF UI/sf-ui-text-regular.ttf
Normal file
BIN
ios/assets/fonts/SourceHanSansCN-Bold.otf
Normal file
BIN
ios/assets/fonts/SourceHanSansCN-Medium.otf
Normal file
BIN
ios/assets/fonts/SourceHanSansCN-Regular.otf
Normal file
BIN
ios/assets/images/3.0x/Contact_Female.webp
Normal file
After Width: | Height: | Size: 578 B |
BIN
ios/assets/images/3.0x/Contact_Male.webp
Normal file
After Width: | Height: | Size: 556 B |
BIN
ios/assets/images/3.0x/add_addressicon.png
Normal file
After Width: | Height: | Size: 448 B |
BIN
ios/assets/images/3.0x/bar_close.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
ios/assets/images/3.0x/bsc.webp
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
ios/assets/images/3.0x/contacts_add_newmessage.png
Normal file
After Width: | Height: | Size: 505 B |
BIN
ios/assets/images/3.0x/def_avatar.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ios/assets/images/3.0x/favorite.webp
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
ios/assets/images/3.0x/ic_add_friend.webp
Normal file
After Width: | Height: | Size: 922 B |
BIN
ios/assets/images/3.0x/ic_delete.webp
Normal file
After Width: | Height: | Size: 420 B |
BIN
ios/assets/images/3.0x/ic_no_data.webp
Normal file
After Width: | Height: | Size: 1020 B |
BIN
ios/assets/images/3.0x/ic_right_arrow_black.webp
Normal file
After Width: | Height: | Size: 180 B |
BIN
ios/assets/images/3.0x/ic_right_arrow_grey.webp
Normal file
After Width: | Height: | Size: 452 B |
BIN
ios/assets/images/3.0x/right_more.png
Normal file
After Width: | Height: | Size: 184 B |
BIN
ios/assets/images/3.0x/search_black.webp
Normal file
After Width: | Height: | Size: 774 B |
BIN
ios/assets/images/3.0x/search_white.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/assets/images/3.0x/sharemore_pay.png
Normal file
After Width: | Height: | Size: 429 B |
BIN
ios/assets/images/3.0x/tabbar_chat_c.webp
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
ios/assets/images/3.0x/tabbar_chat_s.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/assets/images/3.0x/tabbar_contacts_c.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
ios/assets/images/3.0x/tabbar_contacts_s.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/assets/images/3.0x/tabbar_discover_c.webp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
ios/assets/images/3.0x/tabbar_discover_s.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
ios/assets/images/3.0x/tabbar_me_c.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
ios/assets/images/3.0x/tabbar_me_s.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ios/assets/images/calling_face.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
ios/assets/images/chat/ChatRomm_ToolPanel_Icon_Voipvoice@3x.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
ios/assets/images/chat/ChatRomm_ToolPanel_Icon_Wallet@3x.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
ios/assets/images/chat/flip_camera_icon_nor.webp
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
ios/assets/images/chat/ic_Emotion.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
ios/assets/images/chat/ic_Keyboard.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
ios/assets/images/chat/ic_chat_more.webp
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
ios/assets/images/chat/ic_chat_voice.webp
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
ios/assets/images/chat/ic_details_add.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
ios/assets/images/chat/ic_details_camera.webp
Normal file
After Width: | Height: | Size: 706 B |
BIN
ios/assets/images/chat/ic_details_card.webp
Normal file
After Width: | Height: | Size: 622 B |
BIN
ios/assets/images/chat/ic_details_favorite.webp
Normal file
After Width: | Height: | Size: 894 B |
BIN
ios/assets/images/chat/ic_details_file.webp
Normal file
After Width: | Height: | Size: 300 B |
BIN
ios/assets/images/chat/ic_details_localtion.webp
Normal file
After Width: | Height: | Size: 832 B |
BIN
ios/assets/images/chat/ic_details_media.webp
Normal file
After Width: | Height: | Size: 844 B |
BIN
ios/assets/images/chat/ic_details_photo.webp
Normal file
After Width: | Height: | Size: 512 B |
BIN
ios/assets/images/chat/ic_details_red.webp
Normal file
After Width: | Height: | Size: 602 B |
BIN
ios/assets/images/chat/ic_details_transfer.webp
Normal file
After Width: | Height: | Size: 586 B |
BIN
ios/assets/images/chat/ic_details_video.webp
Normal file
After Width: | Height: | Size: 370 B |
BIN
ios/assets/images/chat/ic_voice.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
ios/assets/images/chat/icon_sight_close.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
ios/assets/images/chat/sound_left_0.webp
Normal file
After Width: | Height: | Size: 672 B |
BIN
ios/assets/images/chat/sound_left_1.webp
Normal file
After Width: | Height: | Size: 198 B |
BIN
ios/assets/images/chat/sound_left_2.webp
Normal file
After Width: | Height: | Size: 374 B |
BIN
ios/assets/images/chat/sound_left_3.webp
Normal file
After Width: | Height: | Size: 672 B |
BIN
ios/assets/images/chat/sound_right_0.png
Normal file
After Width: | Height: | Size: 723 B |
BIN
ios/assets/images/chat/sound_right_1.webp
Normal file
After Width: | Height: | Size: 204 B |
BIN
ios/assets/images/chat/sound_right_2.webp
Normal file
After Width: | Height: | Size: 384 B |
BIN
ios/assets/images/chat/sound_right_3.png
Normal file
After Width: | Height: | Size: 475 B |
BIN
ios/assets/images/chat/voice_volume_1.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ios/assets/images/chat/voice_volume_2.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
ios/assets/images/chat/voice_volume_3.webp
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
ios/assets/images/chat/voice_volume_4.webp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
ios/assets/images/chat/voice_volume_5.webp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
ios/assets/images/chat/voice_volume_6.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ios/assets/images/chat/voice_volume_7.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
ios/assets/images/contact/GameCenterH5GameMenuBtn@2x.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
ios/assets/images/contact/GroupVerify_NewInvitation_Icon@3x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ios/assets/images/contact/ic_contact_add.webp
Normal file
After Width: | Height: | Size: 156 B |
BIN
ios/assets/images/contact/ic_contacts_details.png
Normal file
After Width: | Height: | Size: 184 B |
BIN
ios/assets/images/contact/ic_group.webp
Normal file
After Width: | Height: | Size: 702 B |
BIN
ios/assets/images/contact/ic_new_friend.webp
Normal file
After Width: | Height: | Size: 712 B |
BIN
ios/assets/images/contact/ic_no_public.webp
Normal file
After Width: | Height: | Size: 802 B |
BIN
ios/assets/images/contact/ic_offical.webp
Normal file
After Width: | Height: | Size: 586 B |
BIN
ios/assets/images/contact/ic_reda.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ios/assets/images/contact/ic_scanqr.webp
Normal file
After Width: | Height: | Size: 668 B |
BIN
ios/assets/images/contact/ic_search_wework.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
ios/assets/images/contact/ic_tag.webp
Normal file
After Width: | Height: | Size: 580 B |
BIN
ios/assets/images/contact/ic_video.png
Normal file
After Width: | Height: | Size: 440 B |
BIN
ios/assets/images/contact/ic_voice.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/assets/images/discover/ff_Icon_album.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
ios/assets/images/discover/ff_Icon_bottle.webp
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
ios/assets/images/discover/ff_Icon_browse.webp
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ios/assets/images/discover/ff_Icon_nearby.webp
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
ios/assets/images/discover/ff_Icon_qr_code.webp
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/assets/images/discover/ff_Icon_search.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ios/assets/images/discover/ff_Icon_shake.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ios/assets/images/discover/game_center_h5.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
ios/assets/images/discover/mini_program.webp
Normal file
After Width: | Height: | Size: 910 B |
BIN
ios/assets/images/emoji/sg012.png
Normal file
After Width: | Height: | Size: 1.2 KiB |