Flutter: onTapDown function not always called - flutter

I'm new in flutter development. I add onTapdown listener if i perform any clicked action on the screen. It's worked,but the problem is sometime when i clicked,onTapdown function not get called.I don't know what problem i have done.Hope can help me solve this problem.Thank you in advance.
import 'package:flame/game.dart';
import 'package:flame/components/parallax_component.dart';
import 'package:flame/util.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Util flameUtil = Util();
await flameUtil.fullScreen();
runApp(MyGame(flameUtil).widget);
}
class MyGame extends BaseGame {
MyGame(Util flameUtil) {
final images = [
ParallaxImage("space/bg_base.png",repeat: ImageRepeat.repeat,fill: LayerFill.height),
ParallaxImage("space/bg_big_star.png",repeat: ImageRepeat.repeatY,fill: LayerFill.height),
ParallaxImage("space/bg_planet.png",repeat: ImageRepeat.repeat,fill: LayerFill.none),
];
var game = Squres(images);
add(game);
TapGestureRecognizer tapper = TapGestureRecognizer();
tapper.onTapDown = game.onTapDown;
flameUtil.addGestureRecognizer(tapper);
}
}
class Squres extends ParallaxComponent{
Squres(List<ParallaxImage> images) : super(images){
baseSpeed = const Offset(4,0);
layerDelta = const Offset(0,-50);
}
#override
void render(Canvas canvas) {
super.render(canvas);
}
#override
void resize(Size size) {
super.resize(size);
}
#override
void update(double t) {
super.update(t);
}
//not always trigger
void onTapDown(TapDownDetails tap){
print("trigger");
}
}

Make sure you use flame 0.22.0^ and use create your game class like this instead:
class MyGame extends BaseGame with TapDetector {
Squres game;
MyGame() {
final images = [
ParallaxImage("space/bg_base.png",repeat: ImageRepeat.repeat,fill: LayerFill.height),
ParallaxImage("space/bg_big_star.png",repeat: ImageRepeat.repeatY,fill: LayerFill.height),
ParallaxImage("space/bg_planet.png",repeat: ImageRepeat.repeat,fill: LayerFill.none),
];
game = Squres(images);
add(game);
}
#override
void onTapDown(TapUpDetails details) {
print("trigger");
game.onTapDown(details);
}
}
Also, remember that you don't have to override update, resize and render if you only call super on them.

Related

Why removing other sprite after collision not working in Flame?

I have two sprites in the game e.g Player and Enemy. After collision detection why remove(Component) method is not working?
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/geometry.dart';
import 'package:flutter/material.dart';
void main() async {
runApp(GameWidget(
game: CGame(),
));
}
class CGame extends FlameGame with HasCollidables {
#override
Future<void>? onLoad() async {
add(Enemy());
add(Player());
return super.onLoad();
}
}
class Enemy extends SpriteComponent with HasGameRef, HasHitboxes, Collidable {
Enemy() : super(priority: 1);
#override
Future<void>? onLoad() async {
sprite = await Sprite.load('crate.png');
size = Vector2(100, 100);
position = Vector2(200, gameRef.size.y / 3 * 2);
anchor = Anchor.center;
addHitbox(HitboxRectangle());
return super.onLoad();
}
}
class Player extends SpriteComponent with HasGameRef, HasHitboxes, Collidable {
#override
Future<void>? onLoad() async {
sprite = await Sprite.load('player.png');
size = Vector2(100, 100);
position = Vector2(0, gameRef.size.y / 3 * 2);
anchor = Anchor.center;
addHitbox(HitboxRectangle());
return super.onLoad();
}
#override
void update(double dt) {
position += Vector2(1, 0);
}
#override
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
if (other is Enemy) {
print('Player hit the Enemy!');
remove(Enemy()); //<== Why this line is not working?
}
}
}
You are trying to remove a new instance of an enemy component, you have to remove the specific one that you collided with, which in this case is other.
You also want to remove other from the game where it is added, if you do remove on the component you are trying to remove a subcomponent.
#override
void onCollision(Set<Vector2> intersectionPoints, Collidable other) {
if (other is Enemy) {
other.removeFromParent();
// It can also be done like this since you have the `HasGameRef` mixin
// gameRef.remove(other);
}
}
}

Can a Flame game object such as PositionComponent be Collidable and Draggable

Successfully implemented a Draggable component. When adding the Hitbox and Collidable mixins to the class extended by PositionComponent the drag functionality stops working.
Is it possible to have a draggable component that is also collidable?
Flutter version: 2.2.3
Flame version: 1.0.0-releasecandidate.13
main.dart
import 'package:flame/game.dart';
import 'package:flutter/material.dart';
import 'DraggablesGame.dart';
void main() {
runApp(
GameWidget(
game: DraggablesGame(),
),
);
}
DraggablesGame.dart
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'DraggableSquare.dart';
class DraggablesGame extends BaseGame with HasDraggableComponents, HasCollidables {
#override
Future<void> onLoad() async {
add(DraggableSquare());
add(DraggableSquare()..y = 350);
}
}
DraggableSquare.dart
import 'package:flame/components.dart';
import 'package:flame/extensions.dart';
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
import 'package:flutter/material.dart' show Colors;
import 'DraggablesGame.dart';
class DraggableSquare extends PositionComponent
with Draggable, HasGameRef<DraggablesGame>, Hitbox, Collidable {
#override
bool debugMode = true;
DraggableSquare({Vector2? position})
: super(
position: position ?? Vector2.all(100),
size: Vector2.all(100),
);
Vector2? dragDeltaPosition;
bool get isDragging => dragDeltaPosition != null;
#override
void update(double dt) {
super.update(dt);
debugColor = isDragging ? Colors.greenAccent : Colors.purple;
}
#override
bool onDragStart(int pointerId, DragStartInfo info) {
dragDeltaPosition = info.eventPosition.game - position;
return false;
}
#override
bool onDragUpdate(int pointerId, DragUpdateInfo event) {
final dragDeltaPosition = this.dragDeltaPosition;
if (dragDeltaPosition == null) {
return false;
}
position.setFrom(event.eventPosition.game - dragDeltaPosition);
return false;
}
#override
bool onDragEnd(int pointerId, _) {
dragDeltaPosition = null;
return false;
}
#override
bool onDragCancel(int pointerId) {
dragDeltaPosition = null;
return false;
}
}
Update based on answer
Spydon's answer suggested using addHitbox(HitboxRectangle());. This resulted in the following error:
The method 'addHitbox' isn't defined for the type 'DraggableSquare'.
Instead this modified constructor allows for both the dragging and colliding.
Updated DraggableSquare Constructor
DraggableSquare({Vector2? position})
: super(
position: position,
size: Vector2.all(100),
) {
final hitBox = HitboxRectangle();
addShape(hitBox);
}
When you add the Hitbox mixin you also have to add some hitboxes, otherwise it will not be able to know what it should count as a "hit".
The simplest solution is to add an empty hitbox of either the HitboxRectangle or HitboxCircle type. These hitbox will will the full size of the component if you don't define anything more specific in them.
So to add a HitboxRectangle, you modify your constructor to this:
DraggableSquare({Vector2? position})
: super(
position: position ?? Vector2.all(100),
size: Vector2.all(100),
) {
addShape(HitboxRectangle());
}
If you set debugMode = true you will be able to visually see the hitboxes that you add.

Could not called initState and update (riverpod state_notifier)

Problem
I'm using riverpod and state_notifier.
The initState() and update() that StateNotifier has are called and No. The other member functions can be called successfully. However, other member functions can be called successfully.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_todo_list/todo_list_notifier.dart';
import 'package:riverpod_todo_list/todo_list_state.dart';
void main() {
print('start~~');
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends HookWidget {
// ...
}
final todoListProvider = StateNotifierProvider((_) => TodoListNotifier());
class MyHomePage extends HookWidget {
final _controller = TextEditingController();
final todoListNotifier = useProvider(todoListProvider);
final TodoListState _todoListState =
useProvider(todoListProvider.state.select((value) => value));
//...
import 'package:riverpod_todo_list/todo.dart';
import 'package:riverpod_todo_list/todo_list_state.dart';
import 'package:state_notifier/state_notifier.dart';
import 'package:uuid/uuid.dart';
class TodoListNotifier extends StateNotifier<TodoListState> with LocatorMixin {
TodoListNotifier() : super(const TodoListState());
Uuid _uuid = Uuid();
// could not run.
#override
void initState() {
super.initState();
print('init state~~~');
}
// could not run.
#override
void update(Locator watch) {
super.update(watch);
print('update');
}
// could run.
void add(String title) {
Todo todo = Todo(id: _uuid.v4(), title: title);
List<Todo> todoList = []..addAll(state.todoList);
todoList.add(todo);
state = state.copyWith(todoList: todoList);
}
// could run.
void toggleStatus(int index) {
List<Todo> todoList = []..addAll(state.todoList);
todoList[index] = state.todoList[index]
.copyWith(completed: !state.todoList[index].completed);
state = state.copyWith(todoList: todoList);
print('changed toggle~~');
}
}
restarted logs
not put initState() and update() logs.
Performing hot restart...
Restarted application in 464ms.
flutter: start~~
The question is already answered on the Github.
LocatorMixin is not supported by Riverpod.
https://github.com/rrousselGit/river_pod/issues/75#issuecomment-671255330
And it's proposed to note it in the document.
In my opinion, LocatorMixin is not needed to use with Riverpod because of ProvidierReference.
final userRepositoryProvider = Provider((ref) => UserRepository());
final userControllerProvider = StateNotifierProvider((ref) {
return UserController(
// Read userRepositoryProvider and create a UserController from the result
repository: ref.watch(userRepositoryProvider),
);
});

How to pass children class then parent class is required in constructor

I am creating a state machine with Dart,
I am stuck now, I usually did everything in C++, and I would just pass a pointer for a task like this, but in Dart, I am not sure how to do this.
Basically I have created:
class state
{
some methods
}
class MenuState extends State
{
overridden methods
}
now I need that my state machine class would take all children of the state as a correct argument,
So basically what I have written.
class StateMachine
{
StateMachine(State newState)
}
void main()
{
MenuState myMenuState = new MenuSatate();
StateMachine stateM = new StateMachine(myMenuState);
}
I get the error that I cannot pass the myMenuState even it is a child of State. How do I work this around in dart?
The code looks like this
main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flame/util.dart';
import 'package:flutter/services.dart';
import 'package:flutter/gestures.dart';
import 'package:flame/flame.dart';
import 'stateMachine/gameLoop.dart';
void main() async
{
WidgetsFlutterBinding.ensureInitialized();
Util flameUtil = Util();
await flameUtil.fullScreen();
await flameUtil.setOrientation(DeviceOrientation.portraitUp);
MyGame game = new MyGame();
runApp(game.widget);
}
state.dart
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class MyState extends StatefulWidget
{
#override
MyState()
{
initialiaze();
}
void initialiaze() async
{
}
void render(Canvas canvas)
{
}
void update(double t)
{
}
void resize(Size size)
{
}
void onTapDown(TapDownDetails d)
{
}
#override
State<StatefulWidget> createState() {
// TODO: implement createState
throw UnimplementedError();
}
}
menuState.dart
import 'package:flutter/material.dart';
import '../stateMAchine/state.dart';
import 'package:flutter/gestures.dart';
import 'dart:ui';
class MenuState extends State<MyState>
{
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
#override
void initialiaze() async
{
print("menu State initliazed");
}
#override
void render(Canvas canvas)
{
}
#override
void update(double t)
{
}
#override
void resize(Size size)
{
}
#override
void onTapDown(TapDownDetails d)
{
}
}
stateMachine.dart
import "state.dart";
MyState stateReference;
class StateMachine
{
StateMachine(MyState ref)
{
initialiaze(ref);
}
void initialiaze(MyState ref) async
{
ref.initialiaze();
}
}
gameLoop.dart
this is there error comes, then I start to put menuState as an argument
import 'dart:ui';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'stateMachine.dart';
import '../states/menuState.dart';
import 'state.dart';
class MyGame extends Game {
Size screenSize;
double tileSize;
MenuState menuState;
StateMachine stateMachine;
Random rnd;
MyGame()
{
initialiaze();
}
void initialiaze() async
{
resize(await Flame.util.initialDimensions());
rnd = Random();
menuState = new MenuState();
stateMachine = new StateMachine(menuState);
}
void render(Canvas canvas)
{
Rect bgRect = Rect.fromLTWH(0, 0, screenSize.width, screenSize.height);
Paint bgPaint = Paint();
bgPaint.color = Color(0xff576574);
canvas.drawRect(bgRect, bgPaint);
}
void update(double t)
{
}
void resize(Size size)
{
}
void onTapDown(TapDownDetails d)
{
}
}

Send data from native to flutter

Im extending the native implementation of FirebaseMessagingService to wait for push notifications in native android.
I need to start my flutter app when the user clicks the push notification, so.. How can I send data to my flutter app?
In Flutter
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ScreenPage(),
);
}
}
class ScreenPage extends StatefulWidget {
#override
_ScreenPageState createState() => _ScreenPageState();
}
class _ScreenPageState extends State<ScreenPage> {
static const platform = const MethodChannel("myChannel");
#override
void initState() {
platform.setMethodCallHandler(nativeMethodCallHandler);
super.initState();
}
Future<dynamic> nativeMethodCallHandler(MethodCall methodCall) async {
print('Native call!');
switch (methodCall.method) {
case "methodNameItz" :
return "This data from flutter.....";
break;
default:
return "Nothing";
break;
}
}
#override
Widget build(BuildContext context) {
//return ();
}
}
In Java
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
//import io.flutter.view.FlutterNativeView;
public class MyJavaFile extends FlutterActivity {
Button clickMeButton;
MethodChannel channel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
channel = new MethodChannel(getFlutterView(), "myChannel");
setContentView(R.layout.home_activity);
clickMeButton = findViewById(R.id.clickMeButton);
clickMeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
channel.invokeMethod("methodNameItz", null, new MethodChannel.Result() {
#Override
public void success(Object o) {
Log.d("Results", o.toString());
}
#Override
public void error(String s, String s1, Object o) {
}
#Override
public void notImplemented() {
}
});
}
});
}
}
You can still basically look at the firebase messaging example (or the same for android)
Create a Platform Channel in your AppDelegate (or MainActivity on android) and the register for the same channel on the dart/flutter side (maybe main.dart and register a method like onPushClicked)
Listen for your parse messages in your native code
Send them to your dart code using the platform channel (as seen in the above linked example) channel.invokeMethod('onPushClicked', myMessageArguments)
flutter
Map<String,dynamic> data = <String,dynamic>{};
final res = await methodChannel.invokeMethod('getDistance');
data = Map<String,dynamic>.from(res);
final dis = data['distance'] as String;
final tim = data['time'] as String;
ios native
switch call.method {
case "getDistance":
let data =
["distance":routeFormattedDistance,"time":routeFormattedTravelTime]
result(data)
default :
result("")
}