Using UiKitView to show swift UI code in flutter app.
import SwiftUI
struct Snowflake: Identifiable {
var id = UUID()
var x: Double
var y: Double
var scale: Double
var speed: Double
}
struct SnowFallAnimationView: View{
@State private var snowflakes: [Snowflake] = []
@State private var timer: Timer?
var body: some View {
ZStack {
LinearGradient(
gradient: Gradient(colors: [
.black,
.black
]), startPoint: .top, endPoint: UnitPoint.bottom
)
//.ignore safe area
if #available(iOS 15.0, *) {
Canvas { context, size in
for snowflake in snowflakes {
context.draw(Text("❄️").font(.system(size: 10 * snowflake.scale)), at: CGPoint(x: snowflake.x * size.width, y: snowflake.y * size.height))
}
}
} else {
// Fallback on earlier versions
}
}.onAppear() {
startSnowFall()
}
}
func startSnowFall() {
for _ in 0..<50 {
snowflakes.append(
Snowflake(
x: Double.random(in: 0...1),
y: Double.random(in: -0.2...0),
scale: Double.random(in: 0.5...1.5),
speed: Double.random(in: 0.001...0.003)
)
)
}
timer = Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true, block: {
_ in
for i in snowflakes.indices {
snowflakes[i].y += snowflakes[i].speed
if snowflakes[i].y >= 1.2 {
snowflakes[i].y = -0.2
snowflakes[i].x = Double.random(in: 0...1)
}
}
})
}
}
Flutter side.
class NativeViewExample extends StatefulWidget {
const NativeViewExample({super.key});
@override
State<NativeViewExample> createState() => _NativeViewExampleState();
}
class _NativeViewExampleState extends State<NativeViewExample> {
@override
Widget build(BuildContext context) {
const String viewType = '<platform-view-type>';
// Pass parameters to the platform side.
final Map<String, dynamic> creationParams = <String, dynamic>{};
return Stack(
children: [
UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ElevatedButton(
onPressed: () {
// Flutter button functionality
print("Flutter button pressed");
},
child: Text('Flutter Button'),
),
),
),
],
);
}
}
The problem is: Timer is being created every time the SnowFallAnimationView appears on screen. This results in multiple timers running simultaneously, which leads to the snowflakes accelerating over time. (It happens when navigating to another screen and coming back).
Is there's any way to stop the timer (in swift code) when navigating to another screen. Or, Just work with already created timer. (prevent creation of multiple timers).
.appear
may be called more than once within the View life cycle. So, you should create the timer a single time to prevent duplication and invalidate the timer on .disappear
, something like:
var body: some View {
...
.onAppear {
startSnowFall()
}
.onDisappear {
stopSnowFall()
}
}
func startSnowFall() {
guard timer == nil else { return }
...
}
func stopSnowFall() {
guard timer != nil else { return }
timer?.invalidate()
timer = nil
snowflakes.removeAll()
}