Before Width: | Height: | Size: 7.7 KiB |
@ -0,0 +1,209 @@
|
|||||||
|
import 'package:flame/extensions.dart';
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:geometry/geometry.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
/// {@template pathway}
|
||||||
|
/// [Pathway] creates lines of various shapes.
|
||||||
|
///
|
||||||
|
/// [BodyComponent]s such as a Ball can collide and move along a [Pathway].
|
||||||
|
/// {@endtemplate}
|
||||||
|
class Pathway extends BodyComponent with InitialPosition, Layered {
|
||||||
|
Pathway._({
|
||||||
|
// TODO(ruialonso): remove color when assets added.
|
||||||
|
Color? color,
|
||||||
|
required List<List<Vector2>> paths,
|
||||||
|
}) : _paths = paths {
|
||||||
|
paint = Paint()
|
||||||
|
..color = color ?? const Color.fromARGB(0, 0, 0, 0)
|
||||||
|
..style = PaintingStyle.stroke;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a uniform unidirectional (straight) [Pathway].
|
||||||
|
///
|
||||||
|
/// Does so with two [ChainShape] separated by a [width]. Can
|
||||||
|
/// be rotated by a given [rotation] in radians.
|
||||||
|
///
|
||||||
|
/// If [singleWall] is true, just one [ChainShape] is created.
|
||||||
|
factory Pathway.straight({
|
||||||
|
Color? color,
|
||||||
|
required Vector2 start,
|
||||||
|
required Vector2 end,
|
||||||
|
required double width,
|
||||||
|
double rotation = 0,
|
||||||
|
bool singleWall = false,
|
||||||
|
}) {
|
||||||
|
final paths = <List<Vector2>>[];
|
||||||
|
|
||||||
|
// TODO(ruialonso): Refactor repetitive logic
|
||||||
|
final firstWall = [
|
||||||
|
start.clone(),
|
||||||
|
end.clone(),
|
||||||
|
].map((vector) => vector..rotate(rotation)).toList();
|
||||||
|
paths.add(firstWall);
|
||||||
|
|
||||||
|
if (!singleWall) {
|
||||||
|
final secondWall = [
|
||||||
|
start + Vector2(width, 0),
|
||||||
|
end + Vector2(width, 0),
|
||||||
|
].map((vector) => vector..rotate(rotation)).toList();
|
||||||
|
paths.add(secondWall);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pathway._(
|
||||||
|
color: color,
|
||||||
|
paths: paths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an arc [Pathway].
|
||||||
|
///
|
||||||
|
/// The [angle], in radians, specifies the size of the arc. For example, two
|
||||||
|
/// pi returns a complete circumference.
|
||||||
|
///
|
||||||
|
/// Does so with two [ChainShape] separated by a [width]. Which can be
|
||||||
|
/// rotated by a given [rotation] in radians.
|
||||||
|
///
|
||||||
|
/// The outer radius is specified by [radius], whilst the inner one is
|
||||||
|
/// equivalent to the [radius] minus the [width].
|
||||||
|
///
|
||||||
|
/// If [singleWall] is true, just one [ChainShape] is created.
|
||||||
|
factory Pathway.arc({
|
||||||
|
Color? color,
|
||||||
|
required Vector2 center,
|
||||||
|
required double width,
|
||||||
|
required double radius,
|
||||||
|
required double angle,
|
||||||
|
double rotation = 0,
|
||||||
|
bool singleWall = false,
|
||||||
|
}) {
|
||||||
|
final paths = <List<Vector2>>[];
|
||||||
|
|
||||||
|
// TODO(ruialonso): Refactor repetitive logic
|
||||||
|
final outerWall = calculateArc(
|
||||||
|
center: center,
|
||||||
|
radius: radius,
|
||||||
|
angle: angle,
|
||||||
|
offsetAngle: rotation,
|
||||||
|
);
|
||||||
|
paths.add(outerWall);
|
||||||
|
|
||||||
|
if (!singleWall) {
|
||||||
|
final innerWall = calculateArc(
|
||||||
|
center: center,
|
||||||
|
radius: radius - width,
|
||||||
|
angle: angle,
|
||||||
|
offsetAngle: rotation,
|
||||||
|
);
|
||||||
|
paths.add(innerWall);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pathway._(
|
||||||
|
color: color,
|
||||||
|
paths: paths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a bezier curve [Pathway].
|
||||||
|
///
|
||||||
|
/// Does so with two [ChainShape] separated by a [width]. Which can be
|
||||||
|
/// rotated by a given [rotation] in radians.
|
||||||
|
///
|
||||||
|
/// First and last [controlPoints] set the beginning and end of the curve,
|
||||||
|
/// inner points between them set its final shape.
|
||||||
|
///
|
||||||
|
/// If [singleWall] is true, just one [ChainShape] is created.
|
||||||
|
factory Pathway.bezierCurve({
|
||||||
|
Color? color,
|
||||||
|
required List<Vector2> controlPoints,
|
||||||
|
required double width,
|
||||||
|
double rotation = 0,
|
||||||
|
bool singleWall = false,
|
||||||
|
}) {
|
||||||
|
final paths = <List<Vector2>>[];
|
||||||
|
|
||||||
|
// TODO(ruialonso): Refactor repetitive logic
|
||||||
|
final firstWall = calculateBezierCurve(controlPoints: controlPoints)
|
||||||
|
.map((vector) => vector..rotate(rotation))
|
||||||
|
.toList();
|
||||||
|
paths.add(firstWall);
|
||||||
|
|
||||||
|
if (!singleWall) {
|
||||||
|
final secondWall = calculateBezierCurve(
|
||||||
|
controlPoints: controlPoints
|
||||||
|
.map((vector) => vector + Vector2(width, -width))
|
||||||
|
.toList(),
|
||||||
|
).map((vector) => vector..rotate(rotation)).toList();
|
||||||
|
paths.add(secondWall);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pathway._(
|
||||||
|
color: color,
|
||||||
|
paths: paths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an ellipse [Pathway].
|
||||||
|
///
|
||||||
|
/// Does so with two [ChainShape]s separated by a [width]. Can
|
||||||
|
/// be rotated by a given [rotation] in radians.
|
||||||
|
///
|
||||||
|
/// If [singleWall] is true, just one [ChainShape] is created.
|
||||||
|
factory Pathway.ellipse({
|
||||||
|
Color? color,
|
||||||
|
required Vector2 center,
|
||||||
|
required double width,
|
||||||
|
required double majorRadius,
|
||||||
|
required double minorRadius,
|
||||||
|
double rotation = 0,
|
||||||
|
bool singleWall = false,
|
||||||
|
}) {
|
||||||
|
final paths = <List<Vector2>>[];
|
||||||
|
|
||||||
|
// TODO(ruialonso): Refactor repetitive logic
|
||||||
|
final outerWall = calculateEllipse(
|
||||||
|
center: center,
|
||||||
|
majorRadius: majorRadius,
|
||||||
|
minorRadius: minorRadius,
|
||||||
|
).map((vector) => vector..rotate(rotation)).toList();
|
||||||
|
paths.add(outerWall);
|
||||||
|
|
||||||
|
if (!singleWall) {
|
||||||
|
final innerWall = calculateEllipse(
|
||||||
|
center: center,
|
||||||
|
majorRadius: majorRadius - width,
|
||||||
|
minorRadius: minorRadius - width,
|
||||||
|
).map((vector) => vector..rotate(rotation)).toList();
|
||||||
|
paths.add(innerWall);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pathway._(
|
||||||
|
color: color,
|
||||||
|
paths: paths,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<List<Vector2>> _paths;
|
||||||
|
|
||||||
|
/// Constructs different [ChainShape]s to form the [Pathway] shape.
|
||||||
|
List<FixtureDef> createFixtureDefs() {
|
||||||
|
final fixturesDef = <FixtureDef>[];
|
||||||
|
|
||||||
|
for (final path in _paths) {
|
||||||
|
final chain = ChainShape()..createChain(path);
|
||||||
|
fixturesDef.add(FixtureDef(chain));
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixturesDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Body createBody() {
|
||||||
|
final bodyDef = BodyDef()..position = initialPosition;
|
||||||
|
final body = world.createBody(bodyDef);
|
||||||
|
createFixtureDefs().forEach(body.createFixture);
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,4 @@
|
|||||||
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- lib/**/*.gen.dart
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@ -0,0 +1,68 @@
|
|||||||
|
/// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
/// *****************************************************
|
||||||
|
/// FlutterGen
|
||||||
|
/// *****************************************************
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class $AssetsImagesGen {
|
||||||
|
const $AssetsImagesGen();
|
||||||
|
|
||||||
|
AssetGenImage get ball => const AssetGenImage('assets/images/ball.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
class Assets {
|
||||||
|
Assets._();
|
||||||
|
|
||||||
|
static const $AssetsImagesGen images = $AssetsImagesGen();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AssetGenImage extends AssetImage {
|
||||||
|
const AssetGenImage(String assetName)
|
||||||
|
: super(assetName, package: 'pinball_components');
|
||||||
|
|
||||||
|
Image image({
|
||||||
|
Key? key,
|
||||||
|
ImageFrameBuilder? frameBuilder,
|
||||||
|
ImageLoadingBuilder? loadingBuilder,
|
||||||
|
ImageErrorWidgetBuilder? errorBuilder,
|
||||||
|
String? semanticLabel,
|
||||||
|
bool excludeFromSemantics = false,
|
||||||
|
double? width,
|
||||||
|
double? height,
|
||||||
|
Color? color,
|
||||||
|
BlendMode? colorBlendMode,
|
||||||
|
BoxFit? fit,
|
||||||
|
AlignmentGeometry alignment = Alignment.center,
|
||||||
|
ImageRepeat repeat = ImageRepeat.noRepeat,
|
||||||
|
Rect? centerSlice,
|
||||||
|
bool matchTextDirection = false,
|
||||||
|
bool gaplessPlayback = false,
|
||||||
|
bool isAntiAlias = false,
|
||||||
|
FilterQuality filterQuality = FilterQuality.low,
|
||||||
|
}) {
|
||||||
|
return Image(
|
||||||
|
key: key,
|
||||||
|
image: this,
|
||||||
|
frameBuilder: frameBuilder,
|
||||||
|
loadingBuilder: loadingBuilder,
|
||||||
|
errorBuilder: errorBuilder,
|
||||||
|
semanticLabel: semanticLabel,
|
||||||
|
excludeFromSemantics: excludeFromSemantics,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
color: color,
|
||||||
|
colorBlendMode: colorBlendMode,
|
||||||
|
fit: fit,
|
||||||
|
alignment: alignment,
|
||||||
|
repeat: repeat,
|
||||||
|
centerSlice: centerSlice,
|
||||||
|
matchTextDirection: matchTextDirection,
|
||||||
|
gaplessPlayback: gaplessPlayback,
|
||||||
|
isAntiAlias: isAntiAlias,
|
||||||
|
filterQuality: filterQuality,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get path => assetName;
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
library pinball_components;
|
library pinball_components;
|
||||||
|
|
||||||
|
export 'gen/assets.gen.dart';
|
||||||
export 'src/pinball_components.dart';
|
export 'src/pinball_components.dart';
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flame/components.dart';
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
/// {@template ball}
|
||||||
|
/// A solid, [BodyType.dynamic] sphere that rolls and bounces around
|
||||||
|
/// {@endtemplate}
|
||||||
|
class Ball<T extends Forge2DGame> extends BodyComponent<T>
|
||||||
|
with Layered, InitialPosition {
|
||||||
|
/// {@macro ball_body}
|
||||||
|
Ball({
|
||||||
|
required this.baseColor,
|
||||||
|
}) {
|
||||||
|
// TODO(ruimiguel): while developing Ball can be launched by clicking mouse,
|
||||||
|
// and default layer is Layer.all. But on final game Ball will be always be
|
||||||
|
// be launched from Plunger and LauncherRamp will modify it to Layer.board.
|
||||||
|
// We need to see what happens if Ball appears from other place like nest
|
||||||
|
// bumper, it will need to explicit change layer to Layer.board then.
|
||||||
|
layer = Layer.board;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of the [Ball]
|
||||||
|
final Vector2 size = Vector2.all(2);
|
||||||
|
|
||||||
|
/// The base [Color] used to tint this [Ball]
|
||||||
|
final Color baseColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onLoad() async {
|
||||||
|
await super.onLoad();
|
||||||
|
final sprite = await gameRef.loadSprite(Assets.images.ball.keyName);
|
||||||
|
final tint = baseColor.withOpacity(0.5);
|
||||||
|
await add(
|
||||||
|
SpriteComponent(
|
||||||
|
sprite: sprite,
|
||||||
|
size: size,
|
||||||
|
anchor: Anchor.center,
|
||||||
|
)..tint(tint),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Body createBody() {
|
||||||
|
final shape = CircleShape()..radius = size.x / 2;
|
||||||
|
|
||||||
|
final fixtureDef = FixtureDef(shape)..density = 1;
|
||||||
|
|
||||||
|
final bodyDef = BodyDef()
|
||||||
|
..position = initialPosition
|
||||||
|
..userData = this
|
||||||
|
..type = BodyType.dynamic;
|
||||||
|
|
||||||
|
return world.createBody(bodyDef)..createFixture(fixtureDef);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Immediatly and completly [stop]s the ball.
|
||||||
|
///
|
||||||
|
/// The [Ball] will no longer be affected by any forces, including it's
|
||||||
|
/// weight and those emitted from collisions.
|
||||||
|
void stop() {
|
||||||
|
body.setType(BodyType.static);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows the [Ball] to be affected by forces.
|
||||||
|
///
|
||||||
|
/// If previously [stop]ed, the previous ball's velocity is not kept.
|
||||||
|
void resume() {
|
||||||
|
body.setType(BodyType.dynamic);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
export 'ball.dart';
|
||||||
|
export 'initial_position.dart';
|
||||||
|
export 'layer.dart';
|
@ -1,7 +1 @@
|
|||||||
/// {@template pinball_components}
|
export 'components/components.dart';
|
||||||
/// Package with the UI game components for the Pinball Game
|
|
||||||
/// {@endtemplate}
|
|
||||||
class PinballComponents {
|
|
||||||
/// {@macro pinball_components}
|
|
||||||
const PinballComponents();
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<!--
|
||||||
|
Thanks for contributing!
|
||||||
|
|
||||||
|
Provide a description of your changes below and a general summary in the title
|
||||||
|
|
||||||
|
Please look at the following checklist to ensure that your PR can be accepted quickly:
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--- Describe your changes in detail -->
|
||||||
|
|
||||||
|
## Type of Change
|
||||||
|
|
||||||
|
<!--- Put an `x` in all the boxes that apply: -->
|
||||||
|
|
||||||
|
- [ ] ✨ New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change)
|
||||||
|
- [ ] 🧹 Code refactor
|
||||||
|
- [ ] ✅ Build configuration change
|
||||||
|
- [ ] 📝 Documentation
|
||||||
|
- [ ] 🗑️ Chore
|
@ -0,0 +1,10 @@
|
|||||||
|
name: sandbox
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/flutter_package.yml@v1
|
||||||
|
with:
|
||||||
|
flutter_channel: stable
|
||||||
|
flutter_version: 2.10.0
|
@ -0,0 +1,127 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.lock
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/*
|
||||||
|
|
||||||
|
# Visual Studio Code related
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
.settings/
|
||||||
|
.vscode/*
|
||||||
|
|
||||||
|
# Flutter repo-specific
|
||||||
|
/bin/cache/
|
||||||
|
/bin/mingit/
|
||||||
|
/dev/benchmarks/mega_gallery/
|
||||||
|
/dev/bots/.recipe_deps
|
||||||
|
/dev/bots/android_tools/
|
||||||
|
/dev/docs/doc/
|
||||||
|
/dev/docs/flutter.docs.zip
|
||||||
|
/dev/docs/lib/
|
||||||
|
/dev/docs/pubspec.yaml
|
||||||
|
/dev/integration_tests/**/xcuserdata
|
||||||
|
/dev/integration_tests/**/Pods
|
||||||
|
/packages/flutter/coverage/
|
||||||
|
version
|
||||||
|
|
||||||
|
# packages file containing multi-root paths
|
||||||
|
.packages.generated
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
flutter_*.png
|
||||||
|
linked_*.ds
|
||||||
|
unlinked.ds
|
||||||
|
unlinked_spec.ds
|
||||||
|
.fvm/
|
||||||
|
|
||||||
|
# Android related
|
||||||
|
**/android/**/gradle-wrapper.jar
|
||||||
|
**/android/.gradle
|
||||||
|
**/android/captures/
|
||||||
|
**/android/gradlew
|
||||||
|
**/android/gradlew.bat
|
||||||
|
**/android/local.properties
|
||||||
|
**/android/**/GeneratedPluginRegistrant.java
|
||||||
|
**/android/key.properties
|
||||||
|
**/android/.idea/
|
||||||
|
*.jks
|
||||||
|
|
||||||
|
# iOS/XCode related
|
||||||
|
**/ios/**/*.mode1v3
|
||||||
|
**/ios/**/*.mode2v3
|
||||||
|
**/ios/**/*.moved-aside
|
||||||
|
**/ios/**/*.pbxuser
|
||||||
|
**/ios/**/*.perspectivev3
|
||||||
|
**/ios/**/*sync/
|
||||||
|
**/ios/**/.sconsign.dblite
|
||||||
|
**/ios/**/.tags*
|
||||||
|
**/ios/**/.vagrant/
|
||||||
|
**/ios/**/DerivedData/
|
||||||
|
**/ios/**/Icon?
|
||||||
|
**/ios/**/Pods/
|
||||||
|
**/ios/**/.symlinks/
|
||||||
|
**/ios/**/profile
|
||||||
|
**/ios/**/xcuserdata
|
||||||
|
**/ios/.generated/
|
||||||
|
**/ios/Flutter/App.framework
|
||||||
|
**/ios/Flutter/Flutter.framework
|
||||||
|
**/ios/Flutter/Flutter.podspec
|
||||||
|
**/ios/Flutter/Generated.xcconfig
|
||||||
|
**/ios/Flutter/app.flx
|
||||||
|
**/ios/Flutter/app.zip
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
**/ios/Flutter/flutter_assets/
|
||||||
|
**/ios/Flutter/flutter_export_environment.sh
|
||||||
|
**/ios/ServiceDefinitions.json
|
||||||
|
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||||
|
|
||||||
|
# Coverage
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Submodules
|
||||||
|
!pubspec.lock
|
||||||
|
packages/**/pubspec.lock
|
||||||
|
|
||||||
|
# Web related
|
||||||
|
lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Exceptions to the above rules.
|
||||||
|
!**/ios/**/default.mode1v3
|
||||||
|
!**/ios/**/default.mode2v3
|
||||||
|
!**/ios/**/default.pbxuser
|
||||||
|
!**/ios/**/default.perspectivev3
|
||||||
|
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||||
|
!/dev/ci/**/Gemfile.lock
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.idea/codeStyles/
|
||||||
|
!.idea/dictionaries/
|
||||||
|
!.idea/runConfigurations/
|
@ -0,0 +1,10 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 9b2d32b605630f28625709ebd9d78ab3016b2bf6
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Very Good Ventures
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,164 @@
|
|||||||
|
# Sandbox
|
||||||
|
|
||||||
|
![coverage][coverage_badge]
|
||||||
|
[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
|
||||||
|
[![License: MIT][license_badge]][license_link]
|
||||||
|
|
||||||
|
Generated by the [Very Good CLI][very_good_cli_link] 🤖
|
||||||
|
|
||||||
|
A sanbox application where components are showcased and developed in an isolated way
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Getting Started 🚀
|
||||||
|
|
||||||
|
This project contains 3 flavors:
|
||||||
|
|
||||||
|
- development
|
||||||
|
- staging
|
||||||
|
- production
|
||||||
|
|
||||||
|
To run the desired flavor either use the launch configuration in VSCode/Android Studio or use the following commands:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Development
|
||||||
|
$ flutter run --flavor development --target lib/main_development.dart
|
||||||
|
|
||||||
|
# Staging
|
||||||
|
$ flutter run --flavor staging --target lib/main_staging.dart
|
||||||
|
|
||||||
|
# Production
|
||||||
|
$ flutter run --flavor production --target lib/main_production.dart
|
||||||
|
```
|
||||||
|
|
||||||
|
_\*Sandbox works on iOS, Android, Web, and Windows._
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running Tests 🧪
|
||||||
|
|
||||||
|
To run all unit and widget tests use the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ flutter test --coverage --test-randomize-ordering-seed random
|
||||||
|
```
|
||||||
|
|
||||||
|
To view the generated coverage report you can use [lcov](https://github.com/linux-test-project/lcov).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Generate Coverage Report
|
||||||
|
$ genhtml coverage/lcov.info -o coverage/
|
||||||
|
|
||||||
|
# Open Coverage Report
|
||||||
|
$ open coverage/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working with Translations 🌐
|
||||||
|
|
||||||
|
This project relies on [flutter_localizations][flutter_localizations_link] and follows the [official internationalization guide for Flutter][internationalization_link].
|
||||||
|
|
||||||
|
### Adding Strings
|
||||||
|
|
||||||
|
1. To add a new localizable string, open the `app_en.arb` file at `lib/l10n/arb/app_en.arb`.
|
||||||
|
|
||||||
|
```arb
|
||||||
|
{
|
||||||
|
"@@locale": "en",
|
||||||
|
"counterAppBarTitle": "Counter",
|
||||||
|
"@counterAppBarTitle": {
|
||||||
|
"description": "Text shown in the AppBar of the Counter Page"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Then add a new key/value and description
|
||||||
|
|
||||||
|
```arb
|
||||||
|
{
|
||||||
|
"@@locale": "en",
|
||||||
|
"counterAppBarTitle": "Counter",
|
||||||
|
"@counterAppBarTitle": {
|
||||||
|
"description": "Text shown in the AppBar of the Counter Page"
|
||||||
|
},
|
||||||
|
"helloWorld": "Hello World",
|
||||||
|
"@helloWorld": {
|
||||||
|
"description": "Hello World Text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Use the new string
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:sandbox/l10n/l10n.dart';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final l10n = context.l10n;
|
||||||
|
return Text(l10n.helloWorld);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Supported Locales
|
||||||
|
|
||||||
|
Update the `CFBundleLocalizations` array in the `Info.plist` at `ios/Runner/Info.plist` to include the new locale.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
...
|
||||||
|
|
||||||
|
<key>CFBundleLocalizations</key>
|
||||||
|
<array>
|
||||||
|
<string>en</string>
|
||||||
|
<string>es</string>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Translations
|
||||||
|
|
||||||
|
1. For each supported locale, add a new ARB file in `lib/l10n/arb`.
|
||||||
|
|
||||||
|
```
|
||||||
|
├── l10n
|
||||||
|
│ ├── arb
|
||||||
|
│ │ ├── app_en.arb
|
||||||
|
│ │ └── app_es.arb
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add the translated strings to each `.arb` file:
|
||||||
|
|
||||||
|
`app_en.arb`
|
||||||
|
|
||||||
|
```arb
|
||||||
|
{
|
||||||
|
"@@locale": "en",
|
||||||
|
"counterAppBarTitle": "Counter",
|
||||||
|
"@counterAppBarTitle": {
|
||||||
|
"description": "Text shown in the AppBar of the Counter Page"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`app_es.arb`
|
||||||
|
|
||||||
|
```arb
|
||||||
|
{
|
||||||
|
"@@locale": "es",
|
||||||
|
"counterAppBarTitle": "Contador",
|
||||||
|
"@counterAppBarTitle": {
|
||||||
|
"description": "Texto mostrado en la AppBar de la página del contador"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[coverage_badge]: coverage_badge.svg
|
||||||
|
[flutter_localizations_link]: https://api.flutter.dev/flutter/flutter_localizations/flutter_localizations-library.html
|
||||||
|
[internationalization_link]: https://flutter.dev/docs/development/accessibility-and-localization/internationalization
|
||||||
|
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||||
|
[license_link]: https://opensource.org/licenses/MIT
|
||||||
|
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
|
||||||
|
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
|
||||||
|
[very_good_cli_link]: https://github.com/VeryGoodOpenSource/very_good_cli
|
@ -0,0 +1,4 @@
|
|||||||
|
include: package:very_good_analysis/analysis_options.2.4.0.yaml
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
public_member_api_docs: false
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
|
||||||
|
String buildSourceLink(String path) {
|
||||||
|
return 'https://github.com/VGVentures/pinball/tree/main/packages/pinball_components/sandbox/lib/stories/$path';
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicGame extends Forge2DGame {
|
||||||
|
BasicGame() {
|
||||||
|
images.prefix = '';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2022, Very Good Ventures
|
||||||
|
// https://verygood.ventures
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by an MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
import 'package:dashbook/dashbook.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sandbox/stories/stories.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final dashbook = Dashbook(theme: ThemeData.dark());
|
||||||
|
|
||||||
|
addBallStories(dashbook);
|
||||||
|
runApp(dashbook);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:dashbook/dashbook.dart';
|
||||||
|
import 'package:flame/game.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
import 'package:sandbox/stories/ball/basic.dart';
|
||||||
|
|
||||||
|
void addBallStories(Dashbook dashbook) {
|
||||||
|
dashbook.storiesOf('Ball').add(
|
||||||
|
'Basic',
|
||||||
|
(context) => GameWidget(
|
||||||
|
game: BasicBallGame(
|
||||||
|
color: context.colorProperty('color', Colors.blue),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
codeLink: buildSourceLink('ball/basic.dart'),
|
||||||
|
info: BasicBallGame.info,
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
import 'package:sandbox/common/common.dart';
|
||||||
|
|
||||||
|
class BasicBallGame extends BasicGame with TapDetector {
|
||||||
|
BasicBallGame({ required this.color });
|
||||||
|
|
||||||
|
static const info = '''
|
||||||
|
Basic example of how a Ball works, tap anywhere on the
|
||||||
|
screen to spawn a ball into the game.
|
||||||
|
''';
|
||||||
|
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTapUp(TapUpInfo info) {
|
||||||
|
add(Ball(baseColor: color)
|
||||||
|
..initialPosition = info.eventPosition.game,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'ball/ball.dart';
|
@ -0,0 +1,446 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.8.2"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.15.0"
|
||||||
|
dashbook:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dashbook
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.7"
|
||||||
|
device_frame:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_frame
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.2"
|
||||||
|
flame:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flame
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0-releasecandidate.6"
|
||||||
|
flame_forge2d:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flame_forge2d
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.0-releasecandidate.6"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_colorpicker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_colorpicker
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
flutter_markdown:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_markdown
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.9"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
forge2d:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: forge2d
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.0"
|
||||||
|
freezed_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: freezed_annotation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.3"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.4.0"
|
||||||
|
markdown:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: markdown
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.11"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.7.0"
|
||||||
|
ordered_set:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ordered_set
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
|
pinball_components:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: ".."
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "1.0.0+1"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.4"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.11"
|
||||||
|
shared_preferences_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
shared_preferences_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.3"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.0"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.8"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
url_launcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.20"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.15"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.15"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.9"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
very_good_analysis:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: very_good_analysis
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.4"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0+1"
|
||||||
|
sdks:
|
||||||
|
dart: ">=2.16.0 <3.0.0"
|
||||||
|
flutter: ">=2.10.0"
|
@ -0,0 +1,24 @@
|
|||||||
|
name: sandbox
|
||||||
|
description: A sanbox application where components are showcased and developed in an isolated way
|
||||||
|
version: 1.0.0+1
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.16.0 <3.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
dashbook: ^0.1.7
|
||||||
|
flame: ^1.1.0-releasecandidate.6
|
||||||
|
flame_forge2d: ^0.9.0-releasecandidate.6
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
pinball_components:
|
||||||
|
path: ../
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
very_good_analysis: ^2.4.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
After Width: | Height: | Size: 917 B |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 20 KiB |
@ -0,0 +1,104 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
If you are serving your web app in a path other than the root, change the
|
||||||
|
href value below to reflect the base path you are serving from.
|
||||||
|
|
||||||
|
The path provided below has to start and end with a slash "/" in order for
|
||||||
|
it to work correctly.
|
||||||
|
|
||||||
|
For more details:
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||||
|
|
||||||
|
This is a placeholder for base href that will be replaced by the value of
|
||||||
|
the `--base-href` argument provided to `flutter build`.
|
||||||
|
-->
|
||||||
|
<base href="$FLUTTER_BASE_HREF">
|
||||||
|
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||||
|
<meta name="description" content="A new Flutter project.">
|
||||||
|
|
||||||
|
<!-- iOS meta tags & icons -->
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="sandbox">
|
||||||
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
|
<title>sandbox</title>
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||||
|
application. For more information, see:
|
||||||
|
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
||||||
|
<script>
|
||||||
|
var serviceWorkerVersion = null;
|
||||||
|
var scriptLoaded = false;
|
||||||
|
function loadMainDartJs() {
|
||||||
|
if (scriptLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scriptLoaded = true;
|
||||||
|
var scriptTag = document.createElement('script');
|
||||||
|
scriptTag.src = 'main.dart.js';
|
||||||
|
scriptTag.type = 'application/javascript';
|
||||||
|
document.body.append(scriptTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
// Service workers are supported. Use them.
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
// Wait for registration to finish before dropping the <script> tag.
|
||||||
|
// Otherwise, the browser will load the script multiple times,
|
||||||
|
// potentially different versions.
|
||||||
|
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
|
||||||
|
navigator.serviceWorker.register(serviceWorkerUrl)
|
||||||
|
.then((reg) => {
|
||||||
|
function waitForActivation(serviceWorker) {
|
||||||
|
serviceWorker.addEventListener('statechange', () => {
|
||||||
|
if (serviceWorker.state == 'activated') {
|
||||||
|
console.log('Installed new service worker.');
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!reg.active && (reg.installing || reg.waiting)) {
|
||||||
|
// No active web worker and we have installed or are installing
|
||||||
|
// one for the first time. Simply wait for it to activate.
|
||||||
|
waitForActivation(reg.installing || reg.waiting);
|
||||||
|
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
|
||||||
|
// When the app updates the serviceWorkerVersion changes, so we
|
||||||
|
// need to ask the service worker to update.
|
||||||
|
console.log('New service worker available.');
|
||||||
|
reg.update();
|
||||||
|
waitForActivation(reg.installing);
|
||||||
|
} else {
|
||||||
|
// Existing service worker is still good.
|
||||||
|
console.log('Loading app from service worker.');
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If service worker doesn't succeed in a reasonable amount of time,
|
||||||
|
// fallback to plaint <script> tag.
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!scriptLoaded) {
|
||||||
|
console.warn(
|
||||||
|
'Failed to load app from service worker. Falling back to plain <script> tag.',
|
||||||
|
);
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
}, 4000);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Service workers not supported. Just drop the <script> tag.
|
||||||
|
loadMainDartJs();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "sandbox",
|
||||||
|
"short_name": "sandbox",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#0175C2",
|
||||||
|
"theme_color": "#0175C2",
|
||||||
|
"description": "A new Flutter project.",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"prefer_related_applications": false,
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-maskable-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/Icon-maskable-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export 'test_game.dart';
|
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
|
||||||
|
class TestGame extends Forge2DGame {
|
||||||
|
TestGame() {
|
||||||
|
images.prefix = '';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
// ignore_for_file: cascade_invocations
|
||||||
|
|
||||||
|
import 'package:flame_forge2d/flame_forge2d.dart';
|
||||||
|
import 'package:flame_test/flame_test.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:pinball_components/pinball_components.dart';
|
||||||
|
|
||||||
|
import '../../helpers/helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
final flameTester = FlameTester(TestGame.new);
|
||||||
|
|
||||||
|
group('Ball', () {
|
||||||
|
flameTester.test(
|
||||||
|
'loads correctly',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ready();
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
expect(game.contains(ball), isTrue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group('body', () {
|
||||||
|
flameTester.test(
|
||||||
|
'is dynamic',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
expect(ball.body.bodyType, equals(BodyType.dynamic));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
group('can be moved', () {
|
||||||
|
flameTester.test('by its weight', (game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, isNot(equals(ball.initialPosition)));
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('by applying velocity', (game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
ball.body.gravityScale = 0;
|
||||||
|
ball.body.linearVelocity.setValues(10, 10);
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, isNot(equals(ball.initialPosition)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('fixture', () {
|
||||||
|
flameTester.test(
|
||||||
|
'exists',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
expect(ball.body.fixtures[0], isA<Fixture>());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'is dense',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
final fixture = ball.body.fixtures[0];
|
||||||
|
expect(fixture.density, greaterThan(0));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'shape is circular',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
|
||||||
|
final fixture = ball.body.fixtures[0];
|
||||||
|
expect(fixture.shape.shapeType, equals(ShapeType.circle));
|
||||||
|
expect(fixture.shape.radius, equals(1));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'has Layer.all as default filter maskBits',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ready();
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
await game.ready();
|
||||||
|
|
||||||
|
final fixture = ball.body.fixtures[0];
|
||||||
|
expect(fixture.filterData.maskBits, equals(Layer.board.maskBits));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('stop', () {
|
||||||
|
group("can't be moved", () {
|
||||||
|
flameTester.test('by its weight', (game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
ball.stop();
|
||||||
|
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, equals(ball.initialPosition));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
flameTester.test('by applying velocity', (game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
ball.stop();
|
||||||
|
|
||||||
|
ball.body.linearVelocity.setValues(10, 10);
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, equals(ball.initialPosition));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('resume', () {
|
||||||
|
group('can move', () {
|
||||||
|
flameTester.test(
|
||||||
|
'by its weight when previously stopped',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
ball.stop();
|
||||||
|
ball.resume();
|
||||||
|
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, isNot(equals(ball.initialPosition)));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
flameTester.test(
|
||||||
|
'by applying velocity when previously stopped',
|
||||||
|
(game) async {
|
||||||
|
final ball = Ball(baseColor: Colors.blue);
|
||||||
|
await game.ensureAdd(ball);
|
||||||
|
ball.stop();
|
||||||
|
ball.resume();
|
||||||
|
|
||||||
|
ball.body.gravityScale = 0;
|
||||||
|
ball.body.linearVelocity.setValues(10, 10);
|
||||||
|
game.update(1);
|
||||||
|
expect(ball.body.position, isNot(equals(ball.initialPosition)));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
// ignore_for_file: prefer_const_constructors
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:pinball_components/pinball_components.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
group('PinballComponents', () {
|
|
||||||
test('can be instantiated', () {
|
|
||||||
expect(PinballComponents(), isNotNull);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|