From 144ab20b5d768f47cca8018ce87ab12c643a1827 Mon Sep 17 00:00:00 2001 From: kavan trivedi Date: Thu, 10 Aug 2023 20:17:37 +0530 Subject: [PATCH] :sparkles: Added floating animation based on gyroscope's data in IOS --- ios/.gitignore | 38 ++++++++++ ios/Classes/FlutterCreditCardPlugin.swift | 68 +++++++++++++++++ ios/flutter_credit_card.podspec | 23 ++++++ lib/credit_card_background.dart | 2 +- lib/credit_card_widget.dart | 7 +- .../floating_interface_base.dart | 55 -------------- .../floating_native_interface.dart | 73 ------------------- .../glare_effect_widget.dart | 2 +- lib/flutter_credit_card_method_channel.dart | 69 ++++++++++++++++-- ...lutter_credit_card_platform_interface.dart | 27 ++++++- pubspec.yaml | 2 + 11 files changed, 224 insertions(+), 142 deletions(-) create mode 100644 ios/.gitignore create mode 100644 ios/Classes/FlutterCreditCardPlugin.swift create mode 100644 ios/flutter_credit_card.podspec delete mode 100644 lib/floating_card_setup/floating_interface/floating_interface_base.dart delete mode 100644 lib/floating_card_setup/floating_interface/floating_native_interface.dart rename lib/floating_card_setup/{helper_widgets => }/glare_effect_widget.dart (98%) diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..0c88507 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Classes/FlutterCreditCardPlugin.swift b/ios/Classes/FlutterCreditCardPlugin.swift new file mode 100644 index 0000000..2fd7566 --- /dev/null +++ b/ios/Classes/FlutterCreditCardPlugin.swift @@ -0,0 +1,68 @@ +import Flutter +import UIKit +import CoreMotion + +private var motionManager: CMMotionManager? +private var eventChannels = [String: FlutterEventChannel]() +private var streamHandlers = [String: FlutterStreamHandler]() +private func initMotionManager() { + if motionManager == nil { + motionManager = CMMotionManager() + } +} + +private func isGyroscopeAvailable() -> Bool { + initMotionManager() + let gyroAvailable = motionManager?.isGyroAvailable ?? false + return gyroAvailable +} + + +public class FlutterCreditCardPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + + let gyroscopeStreamHandlerName = "com.simform.flutter_credit_card/gyroscope" + let gyroscopeStreamHandler = MTGyroscopeStreamHandler() + streamHandlers[gyroscopeStreamHandlerName] = gyroscopeStreamHandler + + let gyroscopeChannel = FlutterEventChannel(name: gyroscopeStreamHandlerName, binaryMessenger: registrar.messenger()) + gyroscopeChannel.setStreamHandler(gyroscopeStreamHandler) + eventChannels[gyroscopeStreamHandlerName] = gyroscopeChannel + + + let channel = FlutterMethodChannel(name: "com.simform.flutter_credit_card", binaryMessenger: registrar.messenger()) + let instance = FlutterCreditCardPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "isGyroscopeAvailable": + let avaialble = isGyroscopeAvailable() + result(avaialble) + default: + result(FlutterMethodNotImplemented) + } + } +} + +public class MTGyroscopeStreamHandler: NSObject, FlutterStreamHandler { + + public func onListen(withArguments arguments: Any?, eventSink sink: @escaping FlutterEventSink) -> FlutterError? { + initMotionManager() + motionManager?.startGyroUpdates(to: OperationQueue()){ (gyroData, error) in + if let rotationRate = gyroData?.rotationRate { + sink([rotationRate.x,rotationRate.y,rotationRate.z]) + } + } + + return nil + } + // Add the timer to the current run loop. + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + motionManager?.stopGyroUpdates() + return FlutterError() + } + +} + diff --git a/ios/flutter_credit_card.podspec b/ios/flutter_credit_card.podspec new file mode 100644 index 0000000..29d928c --- /dev/null +++ b/ios/flutter_credit_card.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint flutter_credit_card.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'flutter_credit_card' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '11.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/lib/credit_card_background.dart b/lib/credit_card_background.dart index 595b489..f2a5586 100644 --- a/lib/credit_card_background.dart +++ b/lib/credit_card_background.dart @@ -2,7 +2,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_credit_card/floating_card_setup/floating_controller.dart'; -import 'package:flutter_credit_card/floating_card_setup/helper_widgets/glare_effect_widget.dart'; +import 'package:flutter_credit_card/floating_card_setup/glare_effect_widget.dart'; import 'constants.dart'; import 'floating_card_setup/constants.dart'; diff --git a/lib/credit_card_widget.dart b/lib/credit_card_widget.dart index ddf1cb2..8c5cbc4 100644 --- a/lib/credit_card_widget.dart +++ b/lib/credit_card_widget.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_credit_card/constants.dart'; import 'package:flutter_credit_card/extension.dart'; +import 'package:flutter_credit_card/flutter_credit_card_platform_interface.dart'; import 'credit_card_animation.dart'; import 'credit_card_background.dart'; @@ -11,7 +12,6 @@ import 'credit_card_brand.dart'; import 'custom_card_type_icon.dart'; import 'floating_card_setup/floating_controller.dart'; import 'floating_card_setup/floating_event.dart'; -import 'floating_card_setup/floating_interface/floating_interface_base.dart'; import 'glassmorphism_config.dart'; const Map CardTypeIconAsset = { @@ -167,7 +167,8 @@ class CreditCardWidget extends StatefulWidget { final bool isFloatingAnimationEnabled; - static final FloatingPlatform instance = FloatingPlatform.instance; + static final FlutterCreditCardPlatform instance = + FlutterCreditCardPlatform.instance; /// floating animation enabled/disabled @override @@ -298,7 +299,7 @@ class _CreditCardWidgetState extends State // Apply the damping factor — which may equal 1 and have no effect, if damping is null. frontFloatingController.x *= frontFloatingController.floatingBackFactor; frontFloatingController.y *= frontFloatingController.floatingBackFactor; - + // Rotate the matrix by the resulting x and y values. matrix.rotateX(frontFloatingController.x); matrix.rotateY(frontFloatingController.y); diff --git a/lib/floating_card_setup/floating_interface/floating_interface_base.dart b/lib/floating_card_setup/floating_interface/floating_interface_base.dart deleted file mode 100644 index c8928b1..0000000 --- a/lib/floating_card_setup/floating_interface/floating_interface_base.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:async'; - -import 'package:flutter_credit_card/floating_card_setup/floating_event.dart'; -import 'package:flutter_credit_card/floating_card_setup/floating_interface/floating_native_interface.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -/// The common platform interface for the [flutter_credit_card] plugin. -abstract class FloatingPlatform extends PlatformInterface { - /// Constructs a [FloatingPlatform]. - FloatingPlatform() : super(token: _token); - - static final Object _token = Object(); - - static FloatingPlatform _instance = FloatingMethodChannel(); - - /// The default instance of [FloatingPlatform] to use. - /// - /// Defaults to [FloatingMethodChannel]. - static FloatingPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [FloatingPlatform] when they register themselves. - static set instance(FloatingPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Platform features declaration - - /// Detects if the platform is Safari Mobile (iOS or iPad). - bool get isSafariMobile => false; - - /// Indicates whether the gradient is available. - bool get isGradientOverlayAvailable => !isSafariMobile; - - /// Indicates whether the gyroscope is available. - bool get isGyroscopeAvailable => false; - - /// Indicates whether a permission is required to access gyroscope data. - bool get isPermissionRequired => false; - - /// Indicates whether the permission is granted. - bool get isPermissionGranted => false; - - /// The gyroscope stream, if available. - Stream? get floatingStream => null; - - Future initialize() async { - throw UnimplementedError(); - } - - Future requestPermission() async { - throw UnimplementedError(); - } -} diff --git a/lib/floating_card_setup/floating_interface/floating_native_interface.dart b/lib/floating_card_setup/floating_interface/floating_native_interface.dart deleted file mode 100644 index beadaec..0000000 --- a/lib/floating_card_setup/floating_interface/floating_native_interface.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/services.dart'; -import 'package:flutter_credit_card/floating_card_setup/floating_event.dart'; - -import 'floating_interface_base.dart'; - -/// The native implementation of the [FloatingPlatform] interface that uses a [MethodChannel] and an [EventChannel]. -class FloatingMethodChannel extends FloatingPlatform { - static EventChannel? _gyroscopeEventChannel; - - static MethodChannel? _methodChannel; - - static Stream? _gyroscopeStream; - - @override - bool get isSafariMobile => false; - - static bool _isGyroscopeAvailable = true; - - @override - bool get isGyroscopeAvailable => _isGyroscopeAvailable; - - @override - bool get isPermissionGranted => false; - - @override - bool get isPermissionRequired => false; - - @override - Stream? get floatingStream { - try { - _gyroscopeStream ??= _gyroscopeEventChannel - ?.receiveBroadcastStream() - .map((dynamic event) { - final List list = event.cast(); - return FloatingEvent( - type: FloatingType.gyroscope, x: list[0], y: list[1], z: list[2]); - }); - _gyroscopeStream?.listen((FloatingEvent event) {}); - return _gyroscopeStream as Stream; - } catch (e) { - // If a PlatformException is thrown, the plugin is not available on the device. - _isGyroscopeAvailable = false; - return null; - } - } - - @override - Future initialize() async { - if (Platform.isIOS || Platform.isAndroid) { - _methodChannel ??= const MethodChannel('com.simform.flutter_credit_card'); - - _isGyroscopeAvailable = - await _methodChannel!.invokeMethod('isGyroscopeAvailable') ?? - false; - - if (_isGyroscopeAvailable) { - _gyroscopeEventChannel ??= - const EventChannel('com.simform.flutter_credit_card/gyroscope'); - } - } else if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) { - // Desktop platforms should not use the gyroscope events. - _isGyroscopeAvailable = false; - } - - return; - } - - @override - Future requestPermission() async => true; -} diff --git a/lib/floating_card_setup/helper_widgets/glare_effect_widget.dart b/lib/floating_card_setup/glare_effect_widget.dart similarity index 98% rename from lib/floating_card_setup/helper_widgets/glare_effect_widget.dart rename to lib/floating_card_setup/glare_effect_widget.dart index 25d9542..90861d2 100644 --- a/lib/floating_card_setup/helper_widgets/glare_effect_widget.dart +++ b/lib/floating_card_setup/glare_effect_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_credit_card/floating_card_setup/floating_controller.dart'; -import '../constants.dart'; +import 'constants.dart'; class GlareEffectWidget extends StatelessWidget { const GlareEffectWidget({ diff --git a/lib/flutter_credit_card_method_channel.dart b/lib/flutter_credit_card_method_channel.dart index 5e9fac7..8557698 100644 --- a/lib/flutter_credit_card_method_channel.dart +++ b/lib/flutter_credit_card_method_channel.dart @@ -1,17 +1,72 @@ -import 'package:flutter/foundation.dart'; +import 'dart:io'; + import 'package:flutter/services.dart'; +import 'floating_card_setup/floating_event.dart'; import 'flutter_credit_card_platform_interface.dart'; /// An implementation of [FlutterCreditCardPlatform] that uses method channels. class MethodChannelFlutterCreditCard extends FlutterCreditCardPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('flutter_credit_card'); + static EventChannel? _gyroscopeEventChannel; + + static MethodChannel? _methodChannel; + + static Stream? _gyroscopeStream; + + @override + bool get isSafariMobile => false; + + static bool _isGyroscopeAvailable = true; + + @override + bool get isGyroscopeAvailable => _isGyroscopeAvailable; + + @override + bool get isPermissionGranted => false; + + @override + bool get isPermissionRequired => false; @override - Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); - return version; + Stream? get floatingStream { + try { + _gyroscopeStream ??= _gyroscopeEventChannel + ?.receiveBroadcastStream() + .map((dynamic event) { + final List list = event.cast(); + return FloatingEvent( + type: FloatingType.gyroscope, x: list[0], y: list[1], z: list[2]); + }); + _gyroscopeStream?.listen((FloatingEvent event) {}); + return _gyroscopeStream as Stream; + } catch (e) { + // If a PlatformException is thrown, the plugin is not available on the device. + _isGyroscopeAvailable = false; + return null; + } } + + @override + Future initialize() async { + if (Platform.isIOS || Platform.isAndroid) { + _methodChannel ??= const MethodChannel('com.simform.flutter_credit_card'); + + _isGyroscopeAvailable = + await _methodChannel!.invokeMethod('isGyroscopeAvailable') ?? + false; + + _gyroscopeEventChannel ??= + const EventChannel('com.simform.flutter_credit_card/gyroscope'); + + } else if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) { + // Desktop platforms should not use the gyroscope events. + _isGyroscopeAvailable = false; + } + + return; + } + + + @override + Future requestPermission() async => true; } diff --git a/lib/flutter_credit_card_platform_interface.dart b/lib/flutter_credit_card_platform_interface.dart index 12995ac..c1e1014 100644 --- a/lib/flutter_credit_card_platform_interface.dart +++ b/lib/flutter_credit_card_platform_interface.dart @@ -1,5 +1,6 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'floating_card_setup/floating_event.dart'; import 'flutter_credit_card_method_channel.dart'; abstract class FlutterCreditCardPlatform extends PlatformInterface { @@ -23,7 +24,29 @@ abstract class FlutterCreditCardPlatform extends PlatformInterface { _instance = instance; } - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); + /// Detects if the platform is Safari Mobile (iOS or iPad). + bool get isSafariMobile => false; + + /// Indicates whether the gradient is available. + bool get isGradientOverlayAvailable => !isSafariMobile; + + /// Indicates whether the gyroscope is available. + bool get isGyroscopeAvailable => false; + + /// Indicates whether a permission is required to access gyroscope data. + bool get isPermissionRequired => false; + + /// Indicates whether the permission is granted. + bool get isPermissionGranted => false; + + /// The gyroscope stream, if available. + Stream? get floatingStream => null; + + Future initialize() async { + throw UnimplementedError(); + } + + Future requestPermission() async { + throw UnimplementedError(); } } diff --git a/pubspec.yaml b/pubspec.yaml index bd95eba..2cdfda1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,8 @@ flutter: android: package: com.simform.flutter_credit_card pluginClass: FlutterCreditCardPlugin + ios: + pluginClass: FlutterCreditCardPlugin assets: - icons/ fonts: