import 'dart:async'; import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart' as widgets; import 'animation.dart'; import 'sprite.dart'; import 'components/animation_component.dart'; import 'game.dart'; import 'position.dart'; /// Some utilities that did not fit anywhere else. /// /// To use this class, access it via [Flame.util]. class Util { /// Sets the app to be fullscreen (no buttons bar os notifications on top). /// /// Most games should probably be this way. Future fullScreen() { return SystemChrome.setEnabledSystemUIOverlays([]); } /// Sets the preferred orientation (landscape or portrait for the app). /// /// When it opens, it will automatically change orientation to the preferred one (if possible). Future setOrientation(DeviceOrientation orientation) { return SystemChrome.setPreferredOrientations([orientation]); } /// Waits for the initial screen dimensions to be available. /// /// Because of flutter's issue #5259, when the app starts the size might be 0x0. /// This waits for the information to be properly updated. /// /// A best practice would be to implement there resize hooks on your game and components and don't use this at all. /// Make sure your components are able to render and update themselves for any possible screen size. Future initialDimensions() async { // https://github.com/flutter/flutter/issues/5259 // "In release mode we start off at 0x0 but we don't in debug mode" return await Future(() { if (window.physicalSize.isEmpty) { final completer = Completer(); window.onMetricsChanged = () { if (!window.physicalSize.isEmpty) { completer.complete(window.physicalSize / window.devicePixelRatio); } }; return completer.future; } return window.physicalSize / window.devicePixelRatio; }); } /// This properly binds a gesture recognizer to your game. /// /// Use this in order to get it to work in case your app also contains other widgets. void addGestureRecognizer(GestureRecognizer recognizer) { if (GestureBinding.instance == null) { throw Exception( 'GestureBinding is not initialized yet, this probably happened because addGestureRecognizer was called before the runApp method'); } GestureBinding.instance.pointerRouter.addGlobalRoute((PointerEvent e) { if (e is PointerDownEvent) { recognizer.addPointer(e); } }); } /// Utility method to render stuff on a specific place. /// /// Some render methods don't allow to pass a offset. /// This method translate the canvas, draw what you want, and then translate back. void drawWhere(Canvas c, Position p, void Function(Canvas) fn) { c.translate(p.x, p.y); fn(c); c.translate(-p.x, -p.y); } /// Returns a regular Flutter widget representing this animation, rendered with the specified size. /// /// This actually creates an [EmbeddedGameWidget] with a [SimpleGame] whose only content is an [AnimationComponent] created from the provided [animation]. /// You can use this implementation as base to easily create your own widgets based on more complex games. /// This is intended to be used by non-game apps that want to add a sprite sheet animation. widgets.Widget animationAsWidget(Position size, Animation animation) { return EmbeddedGameWidget( SimpleGame(AnimationComponent(size.x, size.y, animation)), size: size); } /// Returns a regular Flutter widget represeting this sprite, rendered with the specified size. /// /// This will create a [CustomPaint] widget using a [CustomPainter] for rendering the [Sprite] /// Be aware that the Sprite must have been loaded, otherwise it can't be rendered widgets.CustomPaint spriteAsWidget(Size size, Sprite sprite) => widgets.CustomPaint(size: size, painter: _SpriteCustomPainter(sprite)); } class _SpriteCustomPainter extends widgets.CustomPainter { Sprite _sprite; _SpriteCustomPainter(this._sprite); @override void paint(Canvas canvas, Size size) { if (_sprite.loaded()) { _sprite.render(canvas, size.width, size.height); } } @override bool shouldRepaint(widgets.CustomPainter old) => false; }