diff --git a/isolate_example/lib/main.dart b/isolate_example/lib/main.dart index b4662a4c4..67d7e1f6b 100644 --- a/isolate_example/lib/main.dart +++ b/isolate_example/lib/main.dart @@ -41,7 +41,7 @@ class StartApp extends StatelessWidget { children: [ PerformancePage(), InfiniteProcessPageStarter(), - DataTransferPage(), + DataTransferPageStarter(), ], ), ), diff --git a/isolate_example/lib/page_three.dart b/isolate_example/lib/page_three.dart index cd9ce5130..ea1e506e0 100644 --- a/isolate_example/lib/page_three.dart +++ b/isolate_example/lib/page_three.dart @@ -11,14 +11,239 @@ // 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 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'dart:isolate'; +import 'dart:math'; -class DataTransferPage extends StatelessWidget { +class DataTransferPageStarter extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( - child: Text('Welcome to the Data Transfer Page'), + return ChangeNotifierProvider( + builder: (context) => DataTransferIsolateController(), + child: DataTransferPage(), ); } } + +class DataTransferPage extends StatelessWidget { + @override + Widget build(context) { + return SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + child: Text( + 'Number Generator Progress', + style: Theme.of(context).textTheme.title, + ), + padding: EdgeInsets.all(8), + ), + LinearProgressIndicator( + value: Provider.of(context) + .progressPercent, + backgroundColor: Colors.grey[200], + ), + Expanded(child: RunningList()), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [createButtons(context)], + ), + ], + ), + ], + ), + ); + } +} + +class DataTransferIsolateController extends ChangeNotifier { + Isolate _newIsolate; + ReceivePort _mIceRP; + SendPort _newIceSP; + + final currentProgress = []; + bool running = false; + Stopwatch _timer = Stopwatch(); + double progressPercent = 0; + + DataTransferIsolateController() { + createIsolate(); + listen(); + } + + Future createIsolate() async { + _mIceRP = ReceivePort(); + _newIsolate = + await Isolate.spawn(_secondIsolateEntryPoint, _mIceRP.sendPort); + } + + void listen() { + _mIceRP.listen((dynamic message) { + if (message is SendPort) _newIceSP = message; + + if (message is int) { + currentProgress.insert( + 0, '$message% - ${_timer.elapsedMilliseconds / 1000} Sec'); + progressPercent = message / 100; + } + + if (message is String && message == 'done') { + running = false; + _timer.stop(); + } + + notifyListeners(); + }); + } + + void generateOnSecondaryIsolate() { + if (running) return; + + running = true; + _timer.reset(); + currentProgress.clear(); + + _timer.start(); + _newIceSP.send('start'); + + notifyListeners(); + } + + Future generateRandomNumbers() async { + if (running) return; + + running = true; + Random rng = Random(); + _timer = Stopwatch(); + + currentProgress.clear(); + _timer.start(); + + List randNums = []; + for (int i = 0; i < 100; i++) { + randNums.clear(); + + for (int j = 0; j < 1000000; j++) { + randNums.add(rng.nextInt(100)); + } + await sendNumbers(randNums); + } + } + + Future sendNumbers(List numList) async { + return Future(() { + _newIceSP.send(numList); + }); + } + + Isolate get newIsolate => _newIsolate; + + void dispose() { + super.dispose(); + _newIsolate?.kill(priority: Isolate.immediate); + _newIsolate = null; + } +} + +class RunningList extends StatelessWidget { + @override + Widget build(BuildContext context) { + final controller = Provider.of(context); + final progress = controller.currentProgress; + + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.grey[200], + ), + child: ListView.builder( + itemCount: progress.length, + itemBuilder: (context, index) { + return Column( + children: [ + Card( + child: ListTile( + title: Text(progress[index]), + ), + color: Colors.lightGreenAccent, + ), + Divider( + color: Colors.blue, + height: 3, + ), + ], + ); + }, + ), + ); + } +} + +Future _secondIsolateEntryPoint(SendPort callerSP) async { + ReceivePort newIceRP = ReceivePort(); + callerSP.send(newIceRP.sendPort); + + newIceRP.listen( + (dynamic message) async { + if (message is String && message == 'start') { + await generateAndSum(callerSP, createNums()); + + callerSP.send('done'); + } else { + await generateAndSum(callerSP, message as List); + + callerSP.send('done'); + } + }, + ); +} + +Iterable createNums() sync* { + final rng = Random(); + for (int i = 0; i < 100000000; i++) { + yield rng.nextInt(100); + } +} + +Future generateAndSum(SendPort callerSP, Iterable iter) async { + int sum = 0; + int count = 0; + + for (int x in iter) { + sum += x; + count++; + if ((count + 1) % 1000000 == 0) { + callerSP.send((count + 1) ~/ 1000000); + } + } + + return sum; +} + +Widget createButtons(BuildContext context) { + final controller = + Provider.of(context, listen: false); + return ButtonBar( + alignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + RaisedButton( + child: const Text('Transfer Data to 2nd Isolate'), + elevation: 8.0, + onPressed: controller.generateRandomNumbers, + ), + RaisedButton( + child: const Text('Generate on 2nd Isolate'), + elevation: 8.0, + onPressed: controller.generateOnSecondaryIsolate, + ), + ], + ), + ], + ); +} diff --git a/isolate_example/lib/page_two.dart b/isolate_example/lib/page_two.dart index 338cf2161..b3a5ef8e4 100644 --- a/isolate_example/lib/page_two.dart +++ b/isolate_example/lib/page_two.dart @@ -110,6 +110,7 @@ class InfiniteProcessIsolateController extends ChangeNotifier { Future start() async { if (_running == false && _paused == false) { await createIsolate(); + if (newIsolate == null) return; listen(); _running = true; notifyListeners();