diff --git a/animations/assets/cat.jpg b/animations/assets/cat.jpg new file mode 100644 index 000000000..014b3d6ff Binary files /dev/null and b/animations/assets/cat.jpg differ diff --git a/animations/assets/wolf.jpg b/animations/assets/wolf.jpg new file mode 100644 index 000000000..43fff9624 Binary files /dev/null and b/animations/assets/wolf.jpg differ diff --git a/animations/lib/main.dart b/animations/lib/main.dart index f4ebf1dd0..527fa877e 100644 --- a/animations/lib/main.dart +++ b/animations/lib/main.dart @@ -1,111 +1,84 @@ import 'package:flutter/material.dart'; +import 'src/basics/animation_controller_demo.dart'; +import 'src/misc/expand_card.dart'; -void main() => runApp(MyApp()); +void main() => runApp(AnimationsSamples()); -class MyApp extends StatelessWidget { - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: MyHomePage(title: 'Flutter Demo Home Page'), - ); - } +class Demo { + final String name; + final String route; + final WidgetBuilder builder; + + Demo(this.name, this.route, this.builder); } -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); +List basicDemos = [ + Demo('Animation Controller', AnimationControllerDemo.routeName, + (context) => AnimationControllerDemo()), +]; - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. +List miscDemos = [ + Demo('Expandable Card', ExpandCardDemo.routeName, + (context) => ExpandCardDemo()), +]; - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". +Map basicDemoRoutes = Map.fromEntries( + basicDemos.map((d) => MapEntry(d.route, d.builder))); - final String title; +Map miscDemoRoutes = Map.fromEntries( + miscDemos.map((d) => MapEntry(d.route, d.builder))); - @override - _MyHomePageState createState() => _MyHomePageState(); -} +Map allRoutes = { + ...basicDemoRoutes, + ...miscDemoRoutes, +}; -class _MyHomePageState extends State { - int _counter = 0; +class AnimationsSamples extends StatelessWidget { + Widget build(BuildContext context) { - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); + return MaterialApp( + title: 'Animations Samples', + theme: ThemeData( + primarySwatch: Colors.deepPurple, + ), + routes: allRoutes, + home: HomePage(), + ); } +} + +class HomePage extends StatelessWidget { + final TextStyle _headerStyle = + TextStyle(fontWeight: FontWeight.bold, fontSize: 24); - @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), + title: Text('Animation Examples'), ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.display1, - ), - ], - ), + body: ListView( + children: [ + ListTile(title: Text('Basics', style: _headerStyle)), + ...basicDemos.map((d) => DemoTile(d)), + ListTile(title: Text('Misc', style: _headerStyle)), + ...miscDemos.map((d) => DemoTile(d)), + ], ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} + +class DemoTile extends StatelessWidget { + final Demo demo; + + DemoTile(this.demo); + + Widget build(BuildContext context) { + return ListTile( + title: Text(demo.name), + onTap: () { + Navigator.pushNamed(context, demo.route); + }, ); } } diff --git a/animations/lib/src/basics/animation_controller_demo.dart b/animations/lib/src/basics/animation_controller_demo.dart new file mode 100644 index 000000000..fbb8bf159 --- /dev/null +++ b/animations/lib/src/basics/animation_controller_demo.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class AnimationControllerDemo extends StatefulWidget { + static const String routeName = '/basics/animation_controller'; + + _AnimationControllerDemoState createState() => + _AnimationControllerDemoState(); +} + +class _AnimationControllerDemoState extends State + with SingleTickerProviderStateMixin { + static const Duration _duration = Duration(seconds: 1); + AnimationController controller; + + void initState() { + super.initState(); + + controller = AnimationController(vsync: this, duration: _duration) + ..addListener(() { + // Marks the widget tree as dirty + setState(() {}); + }); + } + + void dispose() { + super.dispose(); + controller.dispose(); + } + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 200), + child: Text('${controller.value.toStringAsFixed(2)}', + style: TextStyle(fontSize: 24)), + ), + MaterialButton( + color: Theme.of(context).primaryColor, + child: Text( + 'animate', + style: TextStyle(color: Colors.white), + ), + onPressed: () { + if (controller.status == AnimationStatus.completed) { + controller.reverse(); + } else { + controller.forward(); + } + }, + ) + ], + ), + ), + ); + } +} diff --git a/animations/lib/src/misc/expand_card.dart b/animations/lib/src/misc/expand_card.dart new file mode 100644 index 000000000..8ef2468ac --- /dev/null +++ b/animations/lib/src/misc/expand_card.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +class ExpandCardDemo extends StatelessWidget { + static String routeName = '/simple_expandable_card'; + + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Simple Expandable Card'), + ), + body: Center( + child: SimpleExpandableCard(), + ), + ); + } +} + +class SimpleExpandableCard extends StatefulWidget { + _SimpleExpandableCardState createState() => _SimpleExpandableCardState(); +} + +class _SimpleExpandableCardState extends State + with SingleTickerProviderStateMixin { + static const Duration duration = Duration(milliseconds: 300); + bool expanded = false; + + void initState() { + super.initState(); + } + + void _toggleExpanded() { + setState(() { + expanded = !expanded; + }); + } + + double get size => expanded ? 200 : 100; + + Widget build(context) { + return GestureDetector( + onTap: () => _toggleExpanded(), + child: Card( + child: AnimatedCrossFade( + alignment: Alignment.center, + duration: duration, + crossFadeState: + expanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, + firstChild: _ImageContainer( + size: size, image: 'assets/wolf.jpg', duration: duration), + secondChild: _ImageContainer( + size: size, image: 'assets/cat.jpg', duration: duration), + ), + ), + ); + } +} + +class _ImageContainer extends StatelessWidget { + final double size; + final String image; + final Duration duration; + + _ImageContainer({this.size, this.image, this.duration}); + + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: AnimatedContainer( + duration: duration, + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.transparent, + image: DecorationImage( + fit: BoxFit.cover, + image: AssetImage(image), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/animations/pubspec.lock b/animations/pubspec.lock index 7a8a085be..e1ce1c17a 100644 --- a/animations/pubspec.lock +++ b/animations/pubspec.lock @@ -143,4 +143,4 @@ packages: source: hosted version: "2.0.8" sdks: - dart: ">=2.2.2 <3.0.0" + dart: ">=2.3.0 <3.0.0" diff --git a/animations/pubspec.yaml b/animations/pubspec.yaml index daf661c99..8964a0b0a 100644 --- a/animations/pubspec.yaml +++ b/animations/pubspec.yaml @@ -1,72 +1,18 @@ name: animations description: A new Flutter project. - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.3.0 <3.0.0" dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 - dev_dependencies: flutter_test: sdk: flutter - - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + assets: + - assets/ diff --git a/animations/test/widget_test.dart b/animations/test/widget_test.dart deleted file mode 100644 index 2be109532..000000000 --- a/animations/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:animations/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -}