Skip to content

[beta branch] Migrate animation sample #701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions animations/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
Expand All @@ -26,8 +22,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -38,13 +32,11 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand All @@ -57,8 +49,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -68,9 +58,7 @@
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
Expand Down Expand Up @@ -201,7 +189,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
Expand Down Expand Up @@ -253,7 +241,6 @@
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
Expand Down Expand Up @@ -330,7 +317,6 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
Expand Down Expand Up @@ -386,7 +372,6 @@
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion animations/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ class Demo {
final String route;
final WidgetBuilder builder;

const Demo({this.name, this.route, this.builder});
const Demo({
required this.name,
required this.route,
required this.builder,
});
}

final basicDemos = [
Expand Down
6 changes: 3 additions & 3 deletions animations/lib/src/basics/01_animated_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class AnimatedContainerDemo extends StatefulWidget {
}

class _AnimatedContainerDemoState extends State<AnimatedContainerDemo> {
Color color;
double borderRadius;
double margin;
late Color color;
late double borderRadius;
late double margin;

@override
void initState() {
Expand Down
2 changes: 1 addition & 1 deletion animations/lib/src/basics/03_animation_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class _AnimationControllerDemoState extends State<AnimationControllerDemo>
// Widget is not visible.

static const Duration _duration = Duration(seconds: 1);
AnimationController controller;
late final AnimationController controller;

@override
void initState() {
Expand Down
4 changes: 2 additions & 2 deletions animations/lib/src/basics/04_tweens.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class _TweenDemoState extends State<TweenDemo>
with SingleTickerProviderStateMixin {
static const Duration _duration = Duration(seconds: 1);
static const double accountBalance = 1000000;
AnimationController controller;
Animation<double> animation;
late final AnimationController controller;
late final Animation<double> animation;

@override
void initState() {
Expand Down
4 changes: 2 additions & 2 deletions animations/lib/src/basics/05_animated_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo>
static const Color beginColor = Colors.deepPurple;
static const Color endColor = Colors.deepOrange;
Duration duration = Duration(milliseconds: 800);
AnimationController controller;
Animation<Color> animation;
late AnimationController controller;
late Animation<Color?> animation;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious how nulls could be inserted this animation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While doing the conversion, this was a weird part for me as well. ColorTween is a glorified version of Tween<Color?>, which does allow for a null Color value:

https://github.com/flutter/flutter/blob/7c0300a3588d91542366d9efadda26fa2c9d9224/packages/flutter/lib/src/animation/tween.dart#L355

I thought about switching to Tween<Color>, but that would cause us to lose the Color-specific lerp function provided by ColorTween.

In the case of this example, I don't expect the tween to ever produce a null value, but it looks like MaterialButton is perfectly happy to accept one and just use the default color in response:

https://github.com/flutter/flutter/blob/88809aa24720705d1cd8de0bfc46faf932fe0b6e/packages/flutter/lib/src/material/material_button.dart#L180

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oof, the docs for Tween call this out:

  /// See the constructor for details about whether this property may be null
  /// (it varies from subclass to subclass).
  T? begin;

ColorTween and most subclasses allows null intentionally:

  /// We recommend that you do not pass [Colors.transparent] as [begin]
  /// or [end] if you want the effect of fading in or out of transparent.
  /// Instead prefer null. [Colors.transparent] refers to black transparent and
  /// thus will fade out of or into black which is likely unwanted.

To me, this seems weird for users, but at least it's explicit now.


@override
void initState() {
Expand Down
10 changes: 5 additions & 5 deletions animations/lib/src/basics/06_custom_tween.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import 'package:flutter/material.dart';

class TypewriterTween extends Tween<String> {
TypewriterTween({String begin = '', String end})
TypewriterTween({String begin = '', String end = ''})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's weird about this is that it doesn't seem to be possible to implement a Tween with a non-nullable T:

class TypewriterTween extends Tween<String> {
  late String _begin;
  late String _end;

  TypewriterTween({String begin = '', String end = ''}) : _begin = begin, _end = end;

  @override
  String get begin => _begin;
  @override
  String get end => _end;
  @override
  set begin(String s) => _begin = s;
  @override
  set end(String s) => _end = s;

  @override
  String lerp(double t) {
    var cutoff = (end!.length * t).round();
    return end!.substring(0, cutoff);
  }
}

Screen Shot 2021-02-09 at 10 59 30 AM

But I can't seem to reproduce this in DartPad. This sample works just fine:

void main() {
  var b = B('foo');
  print(b.s);
}

class A<T> {
  T? s;
}

class B extends A {
  String _s;
  B(String s) : _s = s;

  String get s => _s;
  set(String s) => _s = s;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this threw me a bit, as you probably saw. Tween takes a single type param, and then defines begin and end with the nullable version of that type parameter.

Copy link
Contributor

@johnpryan johnpryan Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah T? is a subtype of T so it's not clear to me why this isn't working

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the subtype relationship was the reverse (String is a subtype of String?). I'm going to go read the null safety guide again. 😄

: super(begin: begin, end: end);

@override
String lerp(double t) {
var cutoff = (end.length * t).round();
return end.substring(0, cutoff);
var cutoff = (end!.length * t).round();
return end!.substring(0, cutoff);
}
}

Expand All @@ -26,8 +26,8 @@ class _CustomTweenDemoState extends State<CustomTweenDemo>
with SingleTickerProviderStateMixin {
static const Duration _duration = Duration(seconds: 3);
static const String message = loremIpsum;
AnimationController controller;
Animation<String> animation;
late final AnimationController controller;
late final Animation<String> animation;

@override
void initState() {
Expand Down
10 changes: 5 additions & 5 deletions animations/lib/src/basics/07_tween_sequence.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class TweenSequenceDemo extends StatefulWidget {
class _TweenSequenceDemoState extends State<TweenSequenceDemo>
with SingleTickerProviderStateMixin {
static const Duration duration = Duration(seconds: 3);
AnimationController controller;
Animation<Color> animation;
late final AnimationController controller;
late final Animation<Color?> animation;

static final colors = [
Colors.red,
Expand All @@ -31,23 +31,23 @@ class _TweenSequenceDemoState extends State<TweenSequenceDemo>
void initState() {
super.initState();

final sequenceItems = <TweenSequenceItem<Color>>[];
final sequenceItems = <TweenSequenceItem<Color?>>[];

for (var i = 0; i < colors.length; i++) {
final beginColor = colors[i];
final endColor = colors[(i + 1) % colors.length];
final weight = 1 / colors.length;

sequenceItems.add(
TweenSequenceItem<Color>(
TweenSequenceItem<Color?>(
tween: ColorTween(begin: beginColor, end: endColor),
weight: weight,
),
);
}

controller = AnimationController(duration: duration, vsync: this);
animation = TweenSequence<Color>(sequenceItems).animate(controller);
animation = TweenSequence<Color?>(sequenceItems).animate(controller);
}

@override
Expand Down
6 changes: 3 additions & 3 deletions animations/lib/src/basics/08_fade_transition.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class FadeTransitionDemo extends StatefulWidget {

class _FadeTransitionDemoState extends State<FadeTransitionDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
CurvedAnimation _curve;
late final AnimationController _controller;
late final Animation<double> _animation;
late final CurvedAnimation _curve;

@override
void initState() {
Expand Down
56 changes: 24 additions & 32 deletions animations/lib/src/misc/animated_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,31 @@ class AnimatedListDemo extends StatefulWidget {

class _AnimatedListDemoState extends State<AnimatedListDemo> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
final listData = initialListData;
final listData = [
UserModel(0, 'Govind', 'Dixit'),
UserModel(1, 'Greta', 'Stoll'),
UserModel(2, 'Monty', 'Carlo'),
UserModel(3, 'Petey', 'Cruiser'),
UserModel(4, 'Barry', 'Cade'),
];
final initialListSize = 5;

void addUser() {
setState(() {
var index = listData.length;
listData.add(
UserModel(firstName: 'New', lastName: 'Person'),
UserModel(++_maxIdValue, 'New', 'Person'),
);
_listKey.currentState
_listKey.currentState!
.insertItem(index, duration: Duration(milliseconds: 300));
});
}

void deleteUser(int index) {
void deleteUser(int id) {
setState(() {
final index = listData.indexWhere((u) => u.id == id);
var user = listData.removeAt(index);
_listKey.currentState.removeItem(
_listKey.currentState!.removeItem(
index,
(context, animation) {
return FadeTransition(
Expand All @@ -49,7 +57,7 @@ class _AnimatedListDemoState extends State<AnimatedListDemo> {
});
}

Widget _buildItem(UserModel user, [int index]) {
Widget _buildItem(UserModel user) {
return ListTile(
key: ValueKey<UserModel>(user),
title: Text(user.firstName),
Expand All @@ -59,7 +67,7 @@ class _AnimatedListDemoState extends State<AnimatedListDemo> {
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => deleteUser(index),
onPressed: () => deleteUser(user.id),
),
);
}
Expand All @@ -79,11 +87,11 @@ class _AnimatedListDemoState extends State<AnimatedListDemo> {
body: SafeArea(
child: AnimatedList(
key: _listKey,
initialItemCount: initialListData.length,
initialItemCount: 5,
itemBuilder: (context, index, animation) {
return FadeTransition(
opacity: animation,
child: _buildItem(listData[index], index),
child: _buildItem(listData[index]),
);
},
),
Expand All @@ -93,31 +101,15 @@ class _AnimatedListDemoState extends State<AnimatedListDemo> {
}

class UserModel {
const UserModel({this.firstName, this.lastName});
UserModel(
this.id,
this.firstName,
this.lastName,
);

final int id;
final String firstName;
final String lastName;
}

List<UserModel> initialListData = [
UserModel(
firstName: 'Govind',
lastName: 'Dixit',
),
UserModel(
firstName: 'Greta',
lastName: 'Stoll',
),
UserModel(
firstName: 'Monty',
lastName: 'Carlo',
),
UserModel(
firstName: 'Petey',
lastName: 'Cruiser',
),
UserModel(
firstName: 'Barry',
lastName: 'Cade',
),
];
int _maxIdValue = 4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be based off the length of the listData list instead?

Copy link
Contributor Author

@RedBrogdon RedBrogdon Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that would cause problems if items were repeatedly added and removed. For example:

  1. Add a new UserModel, which would get an ID of 5 if the code's using the length of the list
  2. Remove the first UserModel in the list.
  3. Add another UserModel, which would also get an ID of 5, since the list has the same length as it did in Adding JSON example. #1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, right.

Loading