You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/isolate_example/lib/infinite_process_page.dart

270 lines
7.0 KiB

// 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<InfiniteProcessIsolateController>(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<int>(
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<int> _currentResults = [];
bool _created = false;
bool _paused = false;
int get currentMultiplier => _currentMultiplier;
bool get paused => _paused;
bool get created => _created;
List<int> get currentResults => _currentResults;
Future<void> 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<void> 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<InfiniteProcessIsolateController>(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<void> _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<int> doSomeWork() {
var rng = Random();
return Future(() {
var sum = 0;
for (var i = 0; i < 1000; i++) {
sum += rng.nextInt(100);
}
return sum;
});
}