Adding the Flutter Sunflower test (#620)

diff --git a/lib/src/common.dart b/lib/src/common.dart
index 27e0be8..6efcb02 100644
--- a/lib/src/common.dart
+++ b/lib/src/common.dart
@@ -64,6 +64,435 @@
 }
 ''';
 
+// From https://gist.github.com/johnpryan/b6409e10de32b280b8938aa75364fa7b
+const sampleCodeFlutterCounter = '''
+import 'package:flutter/material.dart';
+
+void main() => runApp(MyApp());
+
+class MyApp extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      title: 'Flutter Demo',
+      debugShowCheckedModeBanner: false,
+      theme: ThemeData(
+        primarySwatch: Colors.blue,
+      ),
+      home: MyHomePage(title: 'Flutter Demo Home Page'),
+    );
+  }
+}
+
+class MyHomePage extends StatefulWidget {
+  MyHomePage({Key key, this.title}) : super(key: key);
+
+  final String title;
+
+  @override
+  _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+  int _counter = 0;
+
+  void _incrementCounter() {
+    setState(() {
+      _counter++;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(widget.title),
+      ),
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            Text(
+              'You have pushed the button this many times:',
+            ),
+            Text(
+              '\$_counter',
+              style: Theme.of(context).textTheme.headline4,
+            ),
+          ],
+        ),
+      ),
+      floatingActionButton: FloatingActionButton(
+        onPressed: _incrementCounter,
+        tooltip: 'Increment',
+        child: Icon(Icons.add),
+      ),
+    );
+  }
+}
+''';
+
+// From https://gist.github.com/johnpryan/b3ccb26497ac84895540185935ed5825
+const sampleCodeFlutterSunflower = '''
+import 'package:flutter/material.dart';
+import 'dart:math' as math;
+
+final Color primaryColor = Colors.orange;
+final TargetPlatform platform = TargetPlatform.android;
+
+void main() {
+  runApp(Sunflower());
+}
+
+class SunflowerPainter extends CustomPainter {
+  static const seedRadius = 2.0;
+  static const scaleFactor = 4;
+  static const tau = math.pi * 2;
+
+  static final phi = (math.sqrt(5) + 1) / 2;
+
+  final int seeds;
+
+  SunflowerPainter(this.seeds);
+
+  @override
+  void paint(Canvas canvas, Size size) {
+    var center = size.width / 2;
+
+    for (var i = 0; i < seeds; i++) {
+      var theta = i * tau / phi;
+      var r = math.sqrt(i) * scaleFactor;
+      var x = center + r * math.cos(theta);
+      var y = center - r * math.sin(theta);
+      var offset = Offset(x, y);
+      if (!size.contains(offset)) {
+        continue;
+      }
+      drawSeed(canvas, x, y);
+    }
+  }
+
+  @override
+  bool shouldRepaint(SunflowerPainter oldDelegate) {
+    return oldDelegate.seeds != this.seeds;
+  }
+
+  // Draw a small circle representing a seed centered at (x,y).
+  void drawSeed(Canvas canvas, num x, num y) {
+    var paint = Paint()
+      ..strokeWidth = 2
+      ..style = PaintingStyle.fill
+      ..color = primaryColor;
+    canvas.drawCircle(Offset(x, y), seedRadius, paint);
+  }
+}
+
+class Sunflower extends StatefulWidget {
+  @override
+  State<StatefulWidget> createState() {
+    return _SunflowerState();
+  }
+}
+
+class _SunflowerState extends State<Sunflower> {
+  double seeds = 100.0;
+
+  int get seedCount => seeds.floor();
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      debugShowCheckedModeBanner: false,
+      theme: ThemeData().copyWith(
+        platform: platform,
+        brightness: Brightness.dark,
+        sliderTheme: SliderThemeData.fromPrimaryColors(
+          primaryColor: primaryColor,
+          primaryColorLight: primaryColor,
+          primaryColorDark: primaryColor,
+          valueIndicatorTextStyle: DefaultTextStyle.fallback().style,
+        ),
+      ),
+      home: Scaffold(
+        appBar: AppBar(title: Text("Sunflower")),
+        drawer: Drawer(
+            child: ListView(
+          children: [
+            DrawerHeader(
+              child: Center(
+                child: Container(
+                  child: Text(
+                    "Sunflower 🌻",
+                    style: TextStyle(fontSize: 32),
+                  ),
+                ),
+              ),
+            ),
+          ],
+        )),
+        body: Container(
+          constraints: BoxConstraints.expand(),
+          decoration:
+              BoxDecoration(border: Border.all(color: Colors.transparent)),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.center,
+            mainAxisAlignment: MainAxisAlignment.start,
+            children: [
+              Container(
+                decoration: BoxDecoration(
+                    border: Border.all(color: Colors.transparent)),
+                child: SizedBox(
+                  width: 400,
+                  height: 400,
+                  child: CustomPaint(
+                    painter: SunflowerPainter(seedCount),
+                  ),
+                ),
+              ),
+              Text("Showing \$seedCount seeds"),
+              ConstrainedBox(
+                constraints: BoxConstraints.tightFor(width: 300),
+                child: Slider.adaptive(
+                  min: 20,
+                  max: 2000,
+                  value: seeds,
+                  onChanged: (newValue) {
+                    setState(() {
+                      seeds = newValue;
+                    });
+                  },
+                ),
+              ),
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+}
+''';
+
+// From https://gist.github.com/RedBrogdon/ecb28c29c646b7f38139b1e7f44129b7
+const sampleCodeFlutterDraggableCard = '''
+import 'package:flutter/material.dart';
+import 'package:flutter/physics.dart';
+
+main() {
+  runApp(
+    MaterialApp(
+      debugShowCheckedModeBanner: false,
+      home: PhysicsCardDragDemo(),
+    ),
+  );
+}
+
+class PhysicsCardDragDemo extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text('A draggable card!'),
+      ),
+      body: DraggableCard(
+        child: FlutterLogo(
+          size: 128,
+        ),
+      ),
+    );
+  }
+}
+
+class DraggableCard extends StatefulWidget {
+  final Widget child;
+  DraggableCard({this.child});
+
+  @override
+  _DraggableCardState createState() => _DraggableCardState();
+}
+
+class _DraggableCardState extends State<DraggableCard>
+    with SingleTickerProviderStateMixin {
+  AnimationController _controller;
+  Alignment _dragAlignment = Alignment.center;
+  Animation<Alignment> _animation;
+
+  void _runAnimation(Offset pixelsPerSecond, Size size) {
+    _animation = _controller.drive(
+      AlignmentTween(
+        begin: _dragAlignment,
+        end: Alignment.center,
+      ),
+    );
+
+    final unitsPerSecondX = pixelsPerSecond.dx / size.width;
+    final unitsPerSecondY = pixelsPerSecond.dy / size.height;
+    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);
+    final unitVelocity = unitsPerSecond.distance;
+
+    const spring = SpringDescription(
+      mass: 30,
+      stiffness: 1,
+      damping: 1,
+    );
+
+    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);
+
+    _controller.animateWith(simulation);
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = AnimationController(vsync: this);
+
+    _controller.addListener(() {
+      setState(() {
+        _dragAlignment = _animation.value;
+      });
+    });
+  }
+
+  @override
+  void dispose() {
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final size = MediaQuery.of(context).size;
+    return GestureDetector(
+      onPanDown: (details) {
+        _controller.stop();
+      },
+      onPanUpdate: (details) {
+        setState(() {
+          _dragAlignment += Alignment(
+            details.delta.dx / (size.width / 2),
+            details.delta.dy / (size.height / 2),
+          );
+        });
+      },
+      onPanEnd: (details) {
+        _runAnimation(details.velocity.pixelsPerSecond, size);
+      },
+      child: Align(
+        alignment: _dragAlignment,
+        child: Card(
+          child: widget.child,
+        ),
+      ),
+    );
+  }
+}
+''';
+
+// From https://gist.github.com/RedBrogdon/40308e0a5f47acba46ba62f4d8be2bf4
+const sampleCodeFlutterImplicitAnimations = '''
+import 'package:flutter/material.dart';
+import 'dart:math';
+
+class DiscData {
+  static final _rng = Random();
+
+  double size;
+  Color color;
+  Alignment alignment;
+
+  DiscData() {
+    color = Color.fromARGB(
+      _rng.nextInt(200),
+      _rng.nextInt(255),
+      _rng.nextInt(255),
+      _rng.nextInt(255),
+    );
+    size = _rng.nextDouble() * 40 + 10;
+    alignment = Alignment(
+      _rng.nextDouble() * 2 - 1,
+      _rng.nextDouble() * 2 - 1,
+    );
+  }
+}
+
+void main() async {
+  runApp(
+    MaterialApp(
+      debugShowCheckedModeBanner: false,
+      home: Scaffold(
+        body: Container(
+          color: Color(0xFF15202D),
+          child: SizedBox.expand(
+            child: VariousDiscs(50),
+          ),
+        ),
+      ),
+    ),
+  );
+}
+
+class VariousDiscs extends StatefulWidget {
+  final numberOfDiscs;
+
+  VariousDiscs(this.numberOfDiscs);
+  
+  @override
+  _VariousDiscsState createState() => _VariousDiscsState();
+}
+
+class _VariousDiscsState extends State<VariousDiscs> {
+  final _discs = <DiscData>[];
+
+  @override
+  void initState() {
+    super.initState();
+    _makeDiscs();
+  }
+
+  void _makeDiscs() {
+    _discs.clear();
+    for (int i = 0; i < widget.numberOfDiscs; i++) {
+      _discs.add(DiscData());
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: () => setState(() {
+        _makeDiscs();
+      }),
+      child: Stack(
+        children: [
+          Center(
+            child: Text(
+              'Click a disc!',
+              style: TextStyle(color: Colors.white, fontSize: 50),
+            ),
+          ),
+          for (final disc in _discs)
+            Positioned.fill(
+              child: AnimatedAlign(
+                duration: Duration(milliseconds: 500),
+                curve: Curves.easeInOut,
+                alignment: disc.alignment,
+                child: AnimatedContainer(
+                  duration: Duration(milliseconds: 500),
+                  decoration: BoxDecoration(
+                    color: disc.color,
+                    shape: BoxShape.circle,
+                  ),
+                  height: disc.size,
+                  width: disc.size,
+                ),
+              ),
+            ),
+        ],
+      ),
+    );
+  }
+}
+''';
+
 const sampleCodeMultiFoo = """
 import 'bar.dart';
 
diff --git a/pubspec.lock b/pubspec.lock
index 52f97c3..41bef28 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -14,7 +14,7 @@
       name: _fe_analyzer_shared
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "7.0.0"
+    version: "12.0.0"
   analysis_server_lib:
     dependency: "direct main"
     description:
@@ -28,7 +28,7 @@
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.39.17"
+    version: "0.40.6"
   appengine:
     dependency: "direct main"
     description:
@@ -70,14 +70,14 @@
       name: build
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.0"
+    version: "1.5.1"
   build_config:
     dependency: transitive
     description:
       name: build_config
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.2"
+    version: "0.4.3"
   build_daemon:
     dependency: transitive
     description:
@@ -91,21 +91,21 @@
       name: build_resolvers
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.11"
+    version: "1.4.3"
   build_runner:
     dependency: "direct dev"
     description:
       name: build_runner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.10.2"
+    version: "1.10.6"
   build_runner_core:
     dependency: transitive
     description:
       name: build_runner_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "6.0.1"
+    version: "6.1.1"
   built_collection:
     dependency: transitive
     description:
@@ -183,20 +183,13 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.5"
-  csslib:
-    dependency: transitive
-    description:
-      name: csslib
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.16.2"
   dart_style:
     dependency: transitive
     description:
       name: dart_style
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.3.6"
+    version: "1.3.10"
   dartis:
     dependency: "direct main"
     description:
@@ -267,13 +260,6 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.8.0"
-  html:
-    dependency: transitive
-    description:
-      name: html
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.14.0+4"
   http:
     dependency: "direct main"
     description:
@@ -504,7 +490,7 @@
       name: source_gen
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.7+1"
+    version: "0.9.8"
   source_map_stack_trace:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 03e8705..7b947a1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,7 +6,7 @@
   sdk: ^2.10.4
 
 dependencies:
-  analyzer: ^0.39.17
+  analyzer: ^0.40.6
   analysis_server_lib: ^0.1.4
   appengine: ^0.12.0
   args: ^1.6.0
@@ -26,7 +26,7 @@
 
 dev_dependencies:
   async: ^2.2.0
-  build_runner: ^1.10.0
+  build_runner: ^1.10.6
   codemirror: ^0.5.6
   coverage: ^0.14.0
   grinder: ^0.8.0
diff --git a/test/compiler_test.dart b/test/compiler_test.dart
index ae09202..8a5cecf 100644
--- a/test/compiler_test.dart
+++ b/test/compiler_test.dart
@@ -36,43 +36,57 @@
       expect(result.sourceMap, isNull);
     });
 
-    test('compileDDC simple', () async {
-      final result = await compiler.compileDDC(sampleCode);
-      expect(result.success, true);
-      expect(result.compiledJS, isNotEmpty);
-      expect(result.modulesBaseUrl, isNotEmpty);
+    Future<void> Function() _generateCompilerDDCTest(String sample) =>
+        () async {
+          final result = await compiler.compileDDC(sample);
+          print(result.problems);
 
-      expect(result.compiledJS, contains("define('dartpad_main', ["));
-    });
+          expect(result.success, true);
+          expect(result.compiledJS, isNotEmpty);
+          expect(result.modulesBaseUrl, isNotEmpty);
 
-    test('compileDDC with web', () async {
-      final result = await compiler.compileDDC(sampleCodeWeb);
-      expect(result.success, true);
-      expect(result.compiledJS, isNotEmpty);
-      expect(result.modulesBaseUrl, isNotEmpty);
+          expect(result.compiledJS, contains("define('dartpad_main', ["));
+        };
 
-      expect(result.compiledJS, contains("define('dartpad_main', ["));
-    });
+    test(
+      'compileDDC simple',
+      _generateCompilerDDCTest(sampleCode),
+    );
 
-    test('compileDDC with Flutter', () async {
-      final result = await compiler.compileDDC(sampleCodeFlutter);
-      print(result.problems);
+    test(
+      'compileDDC with web',
+      _generateCompilerDDCTest(sampleCodeWeb),
+    );
 
-      expect(result.success, true);
-      expect(result.compiledJS, isNotEmpty);
-      expect(result.modulesBaseUrl, isNotEmpty);
+    test(
+      'compileDDC with Flutter',
+      _generateCompilerDDCTest(sampleCodeFlutter),
+    );
 
-      expect(result.compiledJS, contains("define('dartpad_main', ["));
-    });
+    test(
+      'compileDDC with Flutter Counter',
+      _generateCompilerDDCTest(sampleCodeFlutterCounter),
+    );
 
-    test('compileDDC with async', () async {
-      final result = await compiler.compileDDC(sampleCodeAsync);
-      expect(result.success, true);
-      expect(result.compiledJS, isNotEmpty);
-      expect(result.modulesBaseUrl, isNotEmpty);
+    test(
+      'compileDDC with Flutter Sunflower',
+      _generateCompilerDDCTest(sampleCodeFlutterSunflower),
+    );
 
-      expect(result.compiledJS, contains("define('dartpad_main', ["));
-    });
+    test(
+      'compileDDC with Flutter Draggable Card',
+      _generateCompilerDDCTest(sampleCodeFlutterDraggableCard),
+    );
+
+    test(
+      'compileDDC with Flutter Implicit Animations',
+      _generateCompilerDDCTest(sampleCodeFlutterImplicitAnimations),
+    );
+
+    test(
+      'compileDDC with async',
+      _generateCompilerDDCTest(sampleCodeAsync),
+    );
 
     test('compileDDC with single error', () async {
       final result = await compiler.compileDDC(sampleCodeError);