Repository: flutter
Issue: Memory leak with IndexedStack containing a UiKitView
Labels: platform-ios,c: performance,a: platform-views,perf: memory,has reproducible steps,P2,team-ios,triaged-ios,found in release: 3.22

### Steps to reproduce

```
git clone git@github.com:ristiisa/indexedstackmemoryleak.git
cd indexedstackmemoryleak
flutter create .
flutter pub get
open ios/Runner.xcworkspace
```

1. run code example using xcode
2. open memory report
3. switch between tab 1 and 2
4. witness memory usage growth when tab 1 is selected

### Expected results

constant memory usage

### Actual results

memory leaks until crash

### Code sample

<details open><summary>Code sample</summary>

```dart
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';

import 'dart:developer' as dev;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(MaterialApp(home: LeakSample()));
}

class LeakSample extends StatefulWidget {
  @override
  State<LeakSample> createState() => _LeakSampleState();
}

class _LeakSampleState extends State<LeakSample> {
  Stream<int>? value;
  int lastValue = 0;

  @override
  initState() {
    super.initState();

    value = Stream.periodic(const Duration(milliseconds: 1000), (_) => lastValue++);
  }
  int index = 0;

  MethodChannel? channel;

  @override
  Widget build(BuildContext context) => Scaffold(
    body: IndexedStack(index: index, children: [
      const Center(child: Text("Check memory usage")),
      StreamBuilder<int>(stream: value, builder: (context, snapshot) => UiKitView(
        viewType: "TestView",
        key: ValueKey(lastValue),
        creationParams: { "value": lastValue },
        creationParamsCodec: const StandardMessageCodec(),
        hitTestBehavior: PlatformViewHitTestBehavior.translucent,
        onPlatformViewCreated: (id) {
          channel = MethodChannel("com.example/testview_$id");
          channel?.setMethodCallHandler((call) async {
            dev.log(call.arguments, name: call.method);
          });
        },
      ))
    ]),
    bottomNavigationBar: BottomNavigationBar(
      items: const [
        BottomNavigationBarItem(
          icon: Icon(Icons.looks_one),
          label: "Tab 1",
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.looks_two),
          label: "Tab 2",
        ),
      ],
      currentIndex: index,
      onTap: (v) => setState(() => index = v),
    )
  );
}
```

```swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    weak var registrar = self.registrar(forPlugin: "TestViewPlugin")

    let factory = TestViewFactory(messenger: registrar!.messenger())
    registrar!.register(factory, withId: "TestView")

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

class TestViewFactory: NSObject, FlutterPlatformViewFactory {
  private var messenger: FlutterBinaryMessenger

  init(messenger: FlutterBinaryMessenger) {
    self.messenger = messenger
    super.init()
  }

  func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
    return FlutterStandardMessageCodec.sharedInstance()
  }

  func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
    return TestView(frame: frame, viewIdentifier: viewId, arguments: args as? [String: Any] ?? [:], binaryMessenger: messenger)
  }
}

class TestView: NSObject, FlutterPlatformView {
  let frame: CGRect
  var viewId: Int64
  var messenger: FlutterBinaryMessenger
  var _view: UIView
  var channel: FlutterMethodChannel?
  let buffer = [UInt8](repeating: 0, count: 100*1024*1024)

  init(frame: CGRect, viewIdentifier viewId: Int64, arguments: [String: Any], binaryMessenger messenger: FlutterBinaryMessenger) {
    self.frame = frame
    self.viewId = viewId
    self.messenger = messenger

    _view = UIView()
    _view.backgroundColor = UIColor.blue
    super.init()

    let value = (arguments["value"] as? Int) ?? -1

    let label = UILabel()
    label.text = "Value: \(value)"
    label.textColor = UIColor.white
    label.textAlignment = .center
    label.translatesAutoresizingMaskIntoConstraints = false
  
    _view.addSubview(label)
    
    NSLayoutConstraint.activate([
      label.centerXAnchor.constraint(equalTo: _view.centerXAnchor),
      label.centerYAnchor.constraint(equalTo: _view.centerYAnchor)
    ])
  }

  func view() -> UIView {
    return _view
  }
}

```

</details>


### Screenshots or Video

<details open>
<summary>Screenshots / Video demonstration</summary>

<img width="974" alt="image" src="https://github.com/flutter/flutter/assets/514571/48972126-0c43-4804-8b16-e0ea835189ef">

https://github.com/flutter/flutter/assets/514571/9ff74e55-3c27-4651-965d-df52bf10e0d2

</details>


### Logs

_No response_

### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[✓] Flutter (Channel stable, 3.19.3, on macOS 14.4 23E214 darwin-arm64, locale en-GB)
    • Flutter version 3.19.3 on channel stable at /Users/user/fvm/versions/stable
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ba39319843 (2 months ago), 2024-03-07 15:22:21 -0600
    • Engine revision 2e4ba9c6fb
    • Dart version 3.3.1
    • DevTools version 2.31.1

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
    • Android SDK at /Users/user/Library/Android/sdk
    • Platform android-34, build-tools 31.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15E204a
    • CocoaPods version 1.15.0

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)

[✓] VS Code (version 1.89.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.88.0

[✓] Connected device (4 available)
    • iPhone 15 Pro (mobile) • A34BE4F3-C4CA-4D58-9542-F55D87BEC5F1 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-17-0 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 14.4 23E214 darwin-arm64
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 125.0.6422.60

[✓] Network resources
    • All expected network resources are available.
```

</details>