diff --git a/isolate_example/lib/main.dart b/isolate_example/lib/main.dart index dc08b7306..85b066dd0 100644 --- a/isolate_example/lib/main.dart +++ b/isolate_example/lib/main.dart @@ -18,9 +18,7 @@ import 'page_one.dart'; import 'page_two.dart'; import 'page_three.dart'; -void main() { - runApp(StartApp()); -} +void main() => runApp(new MaterialApp(home: new StartApp())); class StartApp extends StatelessWidget { @override @@ -42,7 +40,7 @@ class StartApp extends StatelessWidget { body: TabBarView( children: [ PerformancePage(), - InfiniteProcessPage(), + InfiniteProcessPageStarter(), DataTransferPage(), ], ), diff --git a/isolate_example/lib/page_one.dart b/isolate_example/lib/page_one.dart index 78178cf70..1e311404b 100644 --- a/isolate_example/lib/page_one.dart +++ b/isolate_example/lib/page_one.dart @@ -12,20 +12,117 @@ // See the License for the specific language governing permissions and // limitations under the License. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -class PerformancePage extends StatelessWidget { +class PerformancePage extends StatefulWidget { + @override + _PerformancePageState createState() => _PerformancePageState(); +} + +class _PerformancePageState extends State { + Future computeFuture = Future.value(); + @override Widget build(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, - children: [ + children: [ SmoothAnimationWidget(), + Container( + alignment: Alignment.bottomCenter, + padding: EdgeInsets.only(top: 150), + child: Column( + children: [ + FutureBuilder( + future: computeFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + return RaisedButton( + child: const Text('Compute on Main'), + elevation: 8.0, + onPressed: createMainIsolateCallBack(context, snapshot), + ); + }, + ), + FutureBuilder( + future: computeFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + return RaisedButton( + child: const Text('Compute on Secondary'), + elevation: 8.0, + onPressed: + createSecondaryIsolateCallBack(context, snapshot), + ); + }, + ), + ], + ), + ), ], ), ); } + + VoidCallback createMainIsolateCallBack( + BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return () { + setState(() { + computeFuture = computeOnMainIsolate() + ..then((_) { + final snackBar = SnackBar( + content: Text('Main Isolate Done!'), + ); + Scaffold.of(context).showSnackBar(snackBar); + }); + }); + }; + } else { + return null; + } + } + + VoidCallback createSecondaryIsolateCallBack( + BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return () { + setState(() { + computeFuture = computeOnSecondaryIsolate() + ..then((_) { + final snackBar = SnackBar( + content: Text('Secondary Isolate Done!'), + ); + Scaffold.of(context).showSnackBar(snackBar); + }); + }); + }; + } else { + return null; + } + } +} + +Future computeOnMainIsolate() async { + // The isolate will need a little time to disable the buttons before the performance hit. + await Future.delayed(Duration(milliseconds: 100), () => fib(45)); +} + +Future computeOnSecondaryIsolate() async { + await compute(fib, 45); +} + +int fib(int n) { + int number1 = n - 1; + int number2 = n - 2; + + if (n == 1) { + return 0; + } else if (n == 0) { + return 1; + } else { + return (fib(number1) + fib(number2)); + } } class SmoothAnimationWidget extends StatefulWidget { @@ -42,18 +139,17 @@ class SmoothAnimationWidgetState extends State void initState() { super.initState(); - _controller = AnimationController( - duration: const Duration(seconds: 1), - vsync: this, - )..addStatusListener( - (status) { - if (status == AnimationStatus.completed) { - _controller.reverse(); - } else if (status == AnimationStatus.dismissed) { - _controller.forward(); - } - }, - ); + _controller = + AnimationController(duration: const Duration(seconds: 1), vsync: this) + ..addStatusListener( + (status) { + if (status == AnimationStatus.completed) { + _controller.reverse(); + } else if (status == AnimationStatus.dismissed) { + _controller.forward(); + } + }, + ); borderRadius = BorderRadiusTween( begin: BorderRadius.circular(100.0), @@ -72,7 +168,7 @@ class SmoothAnimationWidgetState extends State Widget build(BuildContext context) { return AnimatedBuilder( animation: borderRadius, - builder: (context, child) { + builder: (BuildContext context, Widget child) { return Center( child: Container( child: new FlutterLogo( diff --git a/isolate_example/lib/page_two.dart b/isolate_example/lib/page_two.dart index b6547128c..2e39a5dcd 100644 --- a/isolate_example/lib/page_two.dart +++ b/isolate_example/lib/page_two.dart @@ -13,12 +13,252 @@ // limitations under the License. import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'dart:isolate'; +import 'dart:math'; + +class InfiniteProcessPageStarter extends StatelessWidget { + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + builder: (context) => IsolateController(), + child: InfiniteProcessPage(), + ); + } +} class InfiniteProcessPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( - child: Text('Welcome to the Infinite Process Page'), + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded(child: RunningList()), + SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + newButtons(context), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Switch( + value: + !(Provider.of(context, listen: true) + .paused), + onChanged: (_) => + Provider.of(context, listen: false) + .pausedSwitch(), + activeTrackColor: Colors.lightGreenAccent, + activeColor: Colors.black, + inactiveTrackColor: Colors.deepOrangeAccent, + inactiveThumbColor: Colors.black, + ), + Text('Pause/Resume'), + ], + ), + radioButtonWidget(context), + ], + ), + ), + ], + ); + } +} + +class IsolateController extends ChangeNotifier { + Isolate newIsolate; + ReceivePort mIceRP; + SendPort newIceSP; + Capability capability; + + int _currentMultiplier = 1; + List _currentResults = []; + bool _running = false; + bool _paused = false; + + IsolateController() { + start(); + _running = true; + } + + void createIsolate() async { + mIceRP = ReceivePort(); + newIsolate = await Isolate.spawn(_secondIsolateEntryPoint, mIceRP.sendPort); + } + + void listen() async { + mIceRP.listen((message) { + if (message is SendPort) { + newIceSP = message; + newIceSP.send(_currentMultiplier.toString()); + } else if (message is int) { + setCurrentResults(message); + } + }); + } + + Future start() async { + if (_running == false && _paused == false) { + createIsolate(); + listen(); + _running = true; + notifyListeners(); + } + } + + void terminate() { + newIsolate.kill(); + _running = false; + _currentResults.clear(); + notifyListeners(); + } + + void pausedSwitch() { + (_paused) ? newIsolate.resume(capability) : 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(); + } + + int get multiplier => _currentMultiplier; + + bool get paused => _paused; + + bool get running => _running; + + List get currentResults => _currentResults; +} + +class RunningList extends StatelessWidget { + @override + Widget build(BuildContext context) { + List sums = + Provider.of(context, listen: true).currentResults; + + return DecoratedBox( + decoration: BoxDecoration( + color: (Provider.of(context, listen: true).running == + true && + Provider.of(context, listen: true).paused == + false) + ? Colors.lightGreenAccent + : Colors.deepOrangeAccent, + ), + child: new ListView.builder( + itemCount: sums.length, + itemBuilder: (context, index) { + return ListTile( + title: Text((sums.length - index).toString() + + '. ' + + sums[index].toString()), + ); + }, + ), ); } } + +Future _secondIsolateEntryPoint(SendPort callerSP) async { + int multiplyValue = 1; + + ReceivePort newIceRP = ReceivePort(); + callerSP.send(newIceRP.sendPort); + + newIceRP.listen((message) { + if (message is int) multiplyValue = message; + }); + + int forEnd = 10000; + while (true) { + int sum = 0; + + for (int i = 0; i < forEnd; i++) { + sum += await brokenUpComputation(1000); + } + + forEnd += 10; + callerSP.send(sum * multiplyValue); + } +} + +Future brokenUpComputation(int num) { + Random rng = new Random(); + + return Future(() { + int sum = 0; + + for (int i = 0; i < num; i++) { + sum += rng.nextInt(100); + } + return sum; + }); +} + +Widget newButtons(context) { + return ButtonBar( + alignment: MainAxisAlignment.center, + children: [ + RaisedButton( + child: const Text('Start'), + elevation: 8.0, + onPressed: () => + Provider.of(context, listen: false).start(), + ), + RaisedButton( + child: const Text('Terminate'), + elevation: 8.0, + onPressed: () => + Provider.of(context, listen: false).terminate(), + ), + ], + ); +} + +Widget radioButtonWidget(context) { + return new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Radio( + value: 1, + groupValue: + Provider.of(context, listen: true).multiplier, + onChanged: (_) => Provider.of(context, listen: false) + .setMultiplier(1), + ), + new Text('1x'), + new Radio( + value: 2, + groupValue: + Provider.of(context, listen: true).multiplier, + onChanged: (_) => Provider.of(context, listen: false) + .setMultiplier(2), + ), + new Text('2x'), + new Radio( + value: 3, + groupValue: + Provider.of(context, listen: true).multiplier, + onChanged: (_) => Provider.of(context, listen: false) + .setMultiplier(3), + ), + new Text('3x'), + ], + ); +} diff --git a/isolate_example/pubspec.lock b/isolate_example/pubspec.lock index 003859daf..287a5e174 100644 --- a/isolate_example/pubspec.lock +++ b/isolate_example/pubspec.lock @@ -74,6 +74,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.0" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+1" quiver: dependency: transitive description: diff --git a/isolate_example/pubspec.yaml b/isolate_example/pubspec.yaml index 789f03fda..e9d8c264b 100644 --- a/isolate_example/pubspec.yaml +++ b/isolate_example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2 + provider: ^3.0.0 dev_dependencies: flutter_test: