// Copyright 2019-present the Flutter authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import 'dart:isolate'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class InfiniteProcessPageStarter extends StatelessWidget { const InfiniteProcessPageStarter({super.key}); @override Widget build(context) { return ChangeNotifierProvider( create: (context) => InfiniteProcessIsolateController(), child: const InfiniteProcessPage(), ); } } class InfiniteProcessPage extends StatelessWidget { const InfiniteProcessPage({super.key}); @override Widget build(context) { final controller = Provider.of(context); return SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.all(16.0), child: Text( 'Summation Results', style: Theme.of(context).textTheme.titleLarge, ), ), const Expanded( child: RunningList(), ), Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.all(8.0), child: OverflowBar( spacing: 8.0, alignment: MainAxisAlignment.center, children: [ ElevatedButton( style: ElevatedButton.styleFrom(elevation: 8.0), onPressed: () => controller.start(), child: const Text('Start'), ), ElevatedButton( style: ElevatedButton.styleFrom(elevation: 8.0), onPressed: () => controller.terminate(), child: const Text('Terminate'), ), ], ), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Switch( value: !controller.paused, onChanged: (_) => controller.pausedSwitch(), activeTrackColor: Colors.lightGreenAccent, activeColor: Colors.black, inactiveTrackColor: Colors.deepOrangeAccent, inactiveThumbColor: Colors.black, ), const Text('Pause/Resume'), ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ for (int i = 1; i <= 3; i++) ...[ Radio( value: i, groupValue: controller.currentMultiplier, onChanged: (val) => controller.setMultiplier(val!), ), Text('${i}x') ], ], ), ], ), ], ), ); } } class InfiniteProcessIsolateController extends ChangeNotifier { Isolate? newIsolate; late ReceivePort receivePort; late SendPort newIceSP; Capability? capability; int _currentMultiplier = 1; final List _currentResults = []; bool _created = false; bool _paused = false; int get currentMultiplier => _currentMultiplier; bool get paused => _paused; bool get created => _created; List get currentResults => _currentResults; Future createIsolate() async { receivePort = ReceivePort(); newIsolate = await Isolate.spawn(_secondIsolateEntryPoint, receivePort.sendPort); } void listen() { receivePort.listen((dynamic message) { switch (message) { case SendPort(): newIceSP = message; newIceSP.send(_currentMultiplier); case int(): setCurrentResults(message); } }); } Future start() async { if (_created == false && _paused == false) { await createIsolate(); listen(); _created = true; notifyListeners(); } } void terminate() { newIsolate?.kill(); _created = false; _currentResults.clear(); notifyListeners(); } void pausedSwitch() { if (_paused && capability != null) { newIsolate?.resume(capability!); } else { capability = newIsolate?.pause(); } _paused = !_paused; notifyListeners(); } void setMultiplier(int newMultiplier) { _currentMultiplier = newMultiplier; newIceSP.send(_currentMultiplier); notifyListeners(); } void setCurrentResults(int newNum) { _currentResults.insert(0, newNum); notifyListeners(); } @override void dispose() { newIsolate?.kill(priority: Isolate.immediate); newIsolate = null; super.dispose(); } } class RunningList extends StatelessWidget { const RunningList({super.key}); @override Widget build(context) { final controller = Provider.of(context); var sums = controller.currentResults; return DecoratedBox( decoration: BoxDecoration( color: Colors.grey[200], ), child: ListView.builder( itemCount: sums.length, itemBuilder: (context, index) { return Column( children: [ Card( color: (controller.created && !controller.paused) ? Colors.lightGreenAccent : Colors.deepOrangeAccent, child: ListTile( leading: Text('${sums.length - index}.'), title: Text('${sums[index]}.'), ), ), const Divider( color: Colors.blue, height: 3, ), ], ); }, ), ); } } Future _secondIsolateEntryPoint(SendPort callerSP) async { var multiplyValue = 1; var newIceRP = ReceivePort(); callerSP.send(newIceRP.sendPort); newIceRP.listen((dynamic message) { if (message is int) { multiplyValue = message; } }); // This runs until the isolate is terminated. while (true) { var sum = 0; for (var i = 0; i < 10000; i++) { sum += await doSomeWork(); } callerSP.send(sum * multiplyValue); } } Future doSomeWork() { var rng = Random(); return Future(() { var sum = 0; for (var i = 0; i < 1000; i++) { sum += rng.nextInt(100); } return sum; }); }