{"id":13789967,"url":"https://github.com/letsar/flutter_sidekick","last_synced_at":"2025-04-07T06:07:03.664Z","repository":{"id":41322252,"uuid":"155724699","full_name":"letsar/flutter_sidekick","owner":"letsar","description":"Widgets for creating Hero-like animations between two widgets within the same screen.","archived":false,"fork":false,"pushed_at":"2022-03-26T18:12:01.000Z","size":887,"stargazers_count":296,"open_issues_count":10,"forks_count":39,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-31T04:07:22.535Z","etag":null,"topics":["animation","dart","flutter","flutter-widget","hero"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/letsar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"letsar","patreon":"romainrastel","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://www.buymeacoffee.com/romainrastel","paypal.me/RomainRastel"]}},"created_at":"2018-11-01T14:07:20.000Z","updated_at":"2025-03-23T01:41:04.000Z","dependencies_parsed_at":"2022-08-25T20:20:52.864Z","dependency_job_id":null,"html_url":"https://github.com/letsar/flutter_sidekick","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2Fflutter_sidekick","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2Fflutter_sidekick/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2Fflutter_sidekick/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/letsar%2Fflutter_sidekick/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/letsar","download_url":"https://codeload.github.com/letsar/flutter_sidekick/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247601447,"owners_count":20964864,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","dart","flutter","flutter-widget","hero"],"created_at":"2024-08-03T22:00:35.323Z","updated_at":"2025-04-07T06:07:03.624Z","avatar_url":"https://github.com/letsar.png","language":"Dart","funding_links":["https://github.com/sponsors/letsar","https://patreon.com/romainrastel","https://www.buymeacoffee.com/romainrastel","paypal.me/RomainRastel","https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=QTT34M25RDNL6"],"categories":["Packages"],"sub_categories":[],"readme":"# flutter_sidekick\n\nWidgets for creating Hero-like animations between two widgets within the same screen.\n\n[![Pub](https://img.shields.io/pub/v/flutter_sidekick.svg)](https://pub.dartlang.org/packages/flutter_sidekick)\n[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick\u0026hosted_button_id=QTT34M25RDNL6)\n\n![Logo](https://raw.githubusercontent.com/letsar/flutter_sidekick/master/doc/images/sidekick_logo_220x321.png)\n\n![Overview](https://raw.githubusercontent.com/letsar/flutter_sidekick/master/doc/images/bubble_overview.gif)\n\n## Features\n\n* Hero-like animations.\n* Allow you to specify a different animation for each Sidekick.\n* Widget to manage animations between children of two multi-child widgets.\n\n## Getting started\n\nIn the `pubspec.yaml` of your flutter project, add the following dependency:\nThe latest version is [![Pub](https://img.shields.io/pub/v/flutter_sidekick.svg)](https://pub.dartlang.org/packages/flutter_sidekick)\n\n```yaml\ndependencies:\n  ...\n  flutter_sidekick: ^latest_version\n```\n\nIn your library add the following import:\n\n```dart\nimport 'package:flutter_sidekick/flutter_sidekick.dart';\n```\n\nFor help getting started with Flutter, view the online [documentation](https://flutter.io/).\n\n## Widgets\n\n### Sidekick\n\nThe `Sidekick` widget is **heavily** inspired by the [Hero](https://docs.flutter.io/flutter/widgets/Hero-class.html) widget API.\nTo link two sidekicks, the `targetTag` property of the one denoted as the **source** must be identical to the `tag` property of the other one, denoted the **target**.\nThen to animate sidekicks, you can use the `SidekickController` and one of the *move* function.\n\nThe animation below can be created with the following code:\n\n![Simple Overview](https://raw.githubusercontent.com/letsar/flutter_sidekick/master/doc/images/simple_overview.gif)\n\n```dart\nimport 'package:flutter/material.dart';\nimport 'package:flutter_sidekick/flutter_sidekick.dart';\n\nclass SimpleExample extends StatefulWidget {\n  @override\n  _SimpleExampleState createState() =\u003e _SimpleExampleState();\n}\n\nclass _SimpleExampleState extends State\u003cSimpleExample\u003e\n    with TickerProviderStateMixin {\n  SidekickController controller;\n\n  @override\n  void initState() {\n    super.initState();\n    controller =\n        SidekickController(vsync: this, duration: Duration(seconds: 1));\n  }\n\n  @override\n  void dispose() {\n    controller?.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: \u003cWidget\u003e[\n        Positioned(\n          top: 20.0,\n          left: 20.0,\n          width: 100.0,\n          height: 100.0,\n          child: GestureDetector(\n            onTap: () =\u003e controller.moveToTarget(context),\n            child: Sidekick(\n              tag: 'source',\n              targetTag: 'target',\n              child: Container(\n                color: Colors.blue,\n              ),\n            ),\n          ),\n        ),\n        Positioned(\n          bottom: 20.0,\n          right: 20.0,\n          width: 150.0,\n          height: 100.0,\n          child: GestureDetector(\n            onTap: () =\u003e controller.moveToSource(context),\n            child: Sidekick(\n              tag: 'target',\n              child: Container(\n                color: Colors.blue,\n              ),\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n```\n\n### SidekickTeamBuilder\n\nThe `SidekickTeamBuilder` widget can be used to create complex layouts, where widgets from one container can be moved to another one, and you want the transition to be animated:\n\n![Wrap Overview](https://raw.githubusercontent.com/letsar/flutter_sidekick/master/doc/images/wrap_overview.gif)\n\n```dart\nimport 'package:example/widgets/utils.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_sidekick/flutter_sidekick.dart';\n\nclass Item {\n  Item({\n    this.id,\n  });\n  final int id;\n}\n\nclass WrapExample extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return SidekickTeamBuilder\u003cItem\u003e(\n      initialSourceList: List.generate(20, (i) =\u003e Item(id: i)),\n      builder: (context, sourceBuilderDelegates, targetBuilderDelegates) {\n        return Padding(\n          padding: const EdgeInsets.all(8.0),\n          child: Column(\n            crossAxisAlignment: CrossAxisAlignment.center,\n            children: \u003cWidget\u003e[\n              SizedBox(\n                height: 120.0,\n                child: Wrap(\n                  children: sourceBuilderDelegates\n                      .map((builderDelegate) =\u003e builderDelegate.build(\n                            context,\n                            WrapItem(builderDelegate.message, true),\n                            animationBuilder: (animation) =\u003e CurvedAnimation(\n                                  parent: animation,\n                                  curve: Curves.ease,\n                                ),\n                          ))\n                      .toList(),\n                ),\n              ),\n              Expanded(\n                child: Row(\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  crossAxisAlignment: CrossAxisAlignment.center,\n                  children: \u003cWidget\u003e[\n                    CircleButton(\n                      text: '\u003e',\n                      onPressed: () =\u003e SidekickTeamBuilder.of\u003cItem\u003e(context)\n                          .moveAll(SidekickFlightDirection.toTarget),\n                    ),\n                    SizedBox(width: 60.0, height: 60.0),\n                    CircleButton(\n                      text: '\u003c',\n                      onPressed: () =\u003e SidekickTeamBuilder.of\u003cItem\u003e(context)\n                          .moveAll(SidekickFlightDirection.toSource),\n                    ),\n                  ],\n                ),\n              ),\n              SizedBox(\n                height: 250.0,\n                child: Wrap(\n                  children: targetBuilderDelegates\n                      .map((builderDelegate) =\u003e builderDelegate.build(\n                            context,\n                            WrapItem(builderDelegate.message, false),\n                            animationBuilder: (animation) =\u003e CurvedAnimation(\n                                  parent: animation,\n                                  curve: FlippedCurve(Curves.ease),\n                                ),\n                          ))\n                      .toList(),\n                ),\n              )\n            ],\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass WrapItem extends StatelessWidget {\n  const WrapItem(\n    this.item,\n    this.isSource,\n  ) : size = isSource ? 40.0 : 50.0;\n  final bool isSource;\n  final double size;\n  final Item item;\n\n  @override\n  Widget build(BuildContext context) {\n    return GestureDetector(\n      onTap: () =\u003e SidekickTeamBuilder.of\u003cItem\u003e(context).move(item),\n      child: Padding(\n        padding: const EdgeInsets.all(2.0),\n        child: Container(\n          height: size - 4,\n          width: size - 4,\n          color: _getColor(item.id),\n        ),\n      ),\n    );\n  }\n\n  Color _getColor(int index) {\n    switch (index % 4) {\n      case 0:\n        return Colors.blue;\n      case 1:\n        return Colors.green;\n      case 2:\n        return Colors.yellow;\n      case 3:\n        return Colors.red;\n    }\n    return Colors.indigo;\n  }\n}\n```\n\n## Changelog\n\nPlease see the [Changelog](https://github.com/letsar/flutter_sidekick/blob/master/CHANGELOG.md) page to know what's recently changed.\n\n## Contributions\n\nFeel free to contribute to this project.\n\nIf you find a bug or want a feature, but don't know how to fix/implement it, please fill an [issue](https://github.com/letsar/flutter_sidekick/issues).  \nIf you fixed a bug or implemented a new feature, please send a [pull request](https://github.com/letsar/flutter_sidekick/pulls).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletsar%2Fflutter_sidekick","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fletsar%2Fflutter_sidekick","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fletsar%2Fflutter_sidekick/lists"}