How to load/cache .png on Flutter? - flutter

I've made a simple widget with a progress bar of 10 steps. When I click a button, that progress bar increases:
class SoundBarWidget extends StatefulWidget {
SoundBarWidget({
Key key,
this.height,
this.level,
}) : super(key: key);
double height;
double level = 0;
#override
_SoundBarWidgetState createState() => _SoundBarWidgetState();
}
class _SoundBarWidgetState extends State<SoundBarWidget> {
double barHeight() {
return widget.height == null
? DEFAULT_SOUNDBAR_HEIGHT
: (widget.height > 0 ? widget.height : DEFAULT_SOUNDBAR_HEIGHT);
}
#override
Widget build(BuildContext context) {
Widget w;
if (widget.level == null) {
widget.level = 50;
}
if (widget.level > 90) {
w = Image.asset('soundbar11.png', height: barHeight());
} else if (widget.level > 80) {
w = Image.asset('soundbar10.png', height: barHeight());
} else if (widget.level > 70) {
w = Image.asset('soundbar9.png', height: barHeight());
} else if (widget.level > 60) {
w = Image.asset('soundbar8.png', height: barHeight());
} else if (widget.level > 50) {
w = Image.asset('soundbar7.png', height: barHeight());
} else if (widget.level > 40) {
w = Image.asset('soundbar6.png', height: barHeight());
} else if (widget.level > 30) {
w = Image.asset('soundbar5.png', height: barHeight());
} else if (widget.level > 20) {
w = Image.asset('soundbar4.png', height: barHeight());
} else if (widget.level > 10) {
w = Image.asset('soundbar3.png', height: barHeight());
} else if (widget.level > 0) {
w = Image.asset('soundbar2.png', height: barHeight());
} else {
w = Image.asset('soundbar1.png', height: barHeight());
}
return w;
}
}
The problem is that, the first time a new .png is loaded, Flutter renders an empty container for some milliseconds. So what happens is that the progress bar does a little jump. This happens only on the first load of every .png. When that png is re-rendered it does not happen, so I guess it has something to do with the image not being on cache.
So, how do I load/cache flutter .png images before using them?

The precacheImage function is specifically meant to be used to load an image before it's actually drawn:
List<String> _images = ['soundbar1.png', 'soundbar2.png', 'soundbar3.png'];
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () {
this._loadImages();
});
}
Future<void> _loadImages() async {
_images.forEach((image) async {
await precacheImage(AssetImage(image));
});
}
You can read more about it in the official documentation

Related

return dynamic message from class in flutter

Im trying to build flutter app
that has a feature which is using keypoint of posenet to detect some poses
I start from this code and modify BndBox class
https://heartbeat.fritz.ai/implementing-real-time-pose-estimation-on-mobile-using-flutter-af281d3740df
if (((kx[3] - kx[5]).abs() <= 0.05) &&
((kx[3] - kx[11]).abs() <= 0.1)) {
widget.setMsg = "this is right";
print("this is right");
} else if (((kx[3] - kx[5]) > 0.05 && (kx[3] - kx[5]) < 0.1) &&
(((kx[3] - kx[11]) > 0.1) && (kx[3] - kx[11]) < 0.2)) {
setState(() {
widget.setMsg = "this is right";
});
print("this is wrong ");
setState(() {
widget.setMsg = "this is wrongg";
});
widget.setMsg = "this is wrong";
} else {
setState(() {
widget.setMsg = "undefined";
});
widget.setMsg = "undefined";
print("undetected");
}
}
return lists;
}
return Stack(children: widget.model == posenet ? _renderKeypoints() : null);
now my program return list of widget (the ket point )I also want to return the msg("this is wrong ", "this is right"....
I tried to make the stack return the renderKeypoints() ((list of widget )) and widget text but it doesn't work
tried to make variable "msg="start" in my class and pass it to home class but its just keep "start " and did not change to the messages I assigned it to ...

Unhandled Exception: setState() during building of sub class

Im trying to pass data from a child widget to a parent widget using callback
so IM having this error
setState() or markNeedsBuild() called during build.
and I know its because of calling "callback" set msg before returning the stack(during building of child , superclass call setState :( )
widget.setmsg(msg);
return Stack(children: widget.model == posenet ? _renderKeypoints() : null);
SO how can I return renderKeypoints() widget and return msg to super? or is there a way to print msg immediately in the screen (I get the value of msg after having calculation in renderkeypoint ?
Subclass code :
typedef void IntCallback(String id);
// ignore: must_be_immutable
class BndBox extends StatefulWidget {
final List<dynamic> results;
final int previewH;
final int previewW;
final double screenH;
final double screenW;
final String model;
final IntCallback setmsg;
BndBox(this.results, this.previewH, this.previewW, this.screenH, this.screenW,
this.model, this.setmsg);
#override
_BndBoxState createState() => _BndBoxState();
}
class _BndBoxState extends State<BndBox> {
#override
Widget build(BuildContext context) {
String msg = "";
List<Widget> _renderKeypoints() {
var lists = <Widget>[];
var kx = [10.9];
kx.removeAt(0);
widget.results.forEach((re) {
var list = re["keypoints"].values.map<Widget>((k) {
var _x = k["x"];
var _y = k["y"];
var scaleW, scaleH, x, y;
kx.add(k["x"]);
if (widget.screenH / widget.screenW >
widget.previewH / widget.previewW) {
scaleW = widget.screenH / widget.previewH * widget.previewW;
scaleH = widget.screenH;
print("scaleW");
print(scaleW);
print("scaleH");
print(scaleH);
var difW = (scaleW - widget.screenW) / scaleW;
x = (_x - difW / 2) * scaleW;
y = _y * scaleH;
} else {
scaleH = widget.screenW / widget.previewW * widget.previewH;
scaleW = widget.screenW;
var difH = (scaleH - widget.screenH) / scaleH;
x = _x * scaleW;
y = (_y - difH / 2) * scaleH;
}
return Stack(children: [
Positioned(
left: x - 6,
top: y - 6,
width: 100,
height: 12,
child: Container(
child: Text(
"● ${k["part"]}",
style: TextStyle(
color: Color.fromRGBO(37, 213, 253, 1.0),
fontSize: 12.0,
),
),
),
),
]);
}).toList();
lists..addAll(list);
});
try {
if (((kx[3] - kx[5]).abs() <= 0.05) &&
((kx[3] - kx[11]).abs() <= 0.1)) {
msg = "RIGHT POSE";
} else if (((kx[3] - kx[5]) > 0.05 && (kx[3] - kx[5]) < 0.1) &&
(((kx[3] - kx[11]) > 0.1) && (kx[3] - kx[11]) < 0.2)) {
msg = "WRONG POSE";
} else {
msg = "UNDEFINED";
}
} catch (Exception) {
msg = "EXCEPTION";
}
return lists;
}
// widget.setmsg(msg); //error here
return Stack(
children: widget.model == posenet ? _renderKeypoints() : SizedBox(),
}
}
return Stack(children: widget.model == posenet ? _renderKeypoints() : SizedBox());
the solution does not need call back ,,
what I wanted has been solved
return Stack(children: <Widget>[
Stack(
children: widget.model == posenet ? _renderKeypoints() : null,
),
Text(msg)
]);

Flutter : Timer.periodic running multiple times in each iteration

I am trying to make snake 2 in flutter. And I have used Timer.periodic() for game loop. And I tried specifying duration as 1 seconds. But the code inside the Timer.periodic() runs multiple times in a second. I also tried debugging (though I am terrible at that) and found that the code inside the Timer.periodic() ran multiple times without stepping out of it. Though while debugging this couild happen as the code pauses for input. But I'm not sure about anything .Here is my code -
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
class SnakePage extends StatefulWidget {
#override
_SnakePageState createState() => _SnakePageState();
}
class _SnakePageState extends State<SnakePage> {
int score = 0;
String swipe = '';
bool running = false;
int iterates = 0;
List snake = [
[
[4, 3],
1,
true
],
[
[4, 2],
1,
false
],
[
[4, 1],
1,
false
],
];
// Convert radians to degree
double radians(double degree) {
return ((degree * 180) / pi);
}
void turn(moveEvent) {
double angle = radians(moveEvent.delta.direction);
if (angle >= -45 && angle <= 45) {
this.swipe = 'Swipe Right';
} else if (angle >= 45 && angle <= 135) {
this.swipe = 'Swipe Down';
} else if (angle <= -45 && angle >= -135) {
this.swipe = 'Swipe Up';
} else {
this.swipe = 'Swipe Left';
}
}
int toIndex(coOrdinates) {
return ((coOrdinates[0] + 1) * 10) + coOrdinates[1];
}
void run() {
this.running = true;
Timer.periodic(
Duration(
milliseconds: 500,
), (timer) {
this.setState(() {
this.iterates += 1;
this.swipe = this.iterates.toString();
for (var i = 0; i < this.snake.length; i++) {
this.snake[i][0][1] += 1;
if (this.snake[i][0][1] == 10) {
this.snake[i][0][1] = 0;
}
}
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('WC'),
),
body: Listener(
onPointerMove: this.running
? (moveEvent) => this.turn(moveEvent)
: (moveEvent) => this.run(),// Where the function is being called
child: Container();
);
}
}
And please pardon me for code being a mess and not well commented.
Any Help would be appreciated!
The problem is that, every time you execute the run() method, a new timer is created and you listen for it, again. The old timer is not stopped, so it keeps firing.
The solution is, before you create a timer, cancel the previous one. Something like this:
class _SnakePageState extends State<SnakePage> {
Timer? _myTimer;
void run() {
this.running = true;
_myTimer?.cancel(); //in case we have a timer, we'll cancel it.
_myTimer = Timer.periodic(. // assing new timer to our variable.
Duration(
milliseconds: 500,
), (timer) {
this.setState(() {
this.iterates += 1;
this.swipe = this.iterates.toString();
for (var i = 0; i < this.snake.length; i++) {
this.snake[i][0][1] += 1;
if (this.snake[i][0][1] == 10) {
this.snake[i][0][1] = 0;
}
}
});
});
}
}

Allow only enter 3 decimal number flutter

I want to force user to enters only one dot and 3 decimal points.
I found code below:
class NumberRemoveExtraDotFormatter extends TextInputFormatter {
NumberRemoveExtraDotFormatter({this.decimalRange = 3}) : assert(decimalRange == null || decimalRange > 0);
final int decimalRange;
#override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
var nValue = newValue.text;
var nSelection = newValue.selection;
Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)');
nValue = p.allMatches(nValue).map<String>((Match match) => match.group(0)).join();
if (nValue.startsWith('.')) {
nValue = '0.';
} else if (nValue.contains('.')) {
if (nValue.substring(nValue.indexOf('.') + 1).length > decimalRange) {
nValue = oldValue.text;
} else {
if (nValue.split('.').length > 2) {
var split = nValue.split('.');
nValue = split[0] + '.' + split[1];
}
}
}
nSelection = newValue.selection.copyWith(
baseOffset: math.min(nValue.length, nValue.length + 1),
extentOffset: math.min(nValue.length, nValue.length + 1),
);
return TextEditingValue(text: Utils.addCommad(nValue), selection: nSelection, composing: TextRange.empty);
}
}
but the problem is when user enters more than 3 decimal points and then want to remove, it doesn't. because numbers save in textformfield and they to remove until they reach to 3 decimal points and also when typing from middle of input cursor jump to end.
Also I want to shift number out from right if user enter more than 3 decimal points.
How can I achieve this?
If you just want to force user to enters only one dot and 3 decimal points, this could work.
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,3}'))
According to your comment:
How to add thousands separator?
Shifting number out not work. I want to shift number out if user start to typing in decimal part when decimal point reached at maximum. e.g. current value is 0.333 and user set cursor at second 3 (0.3|33) and type 2. then value must be 0.323.
We can use intl NumberFormat to format the number.
This is my code, I did not have a thorough and detailed test. If you find any bugs, please point them out.
UPDATE
when enter long number whit 0 maximumFractionDigits, wrong number will added. => this is not depends on maximumFractionDigits. it's happening always.
I think there has some unexpected behavior in the NumberFormat, and I changed it to custom method and it support negative number now.
class NumberInputFormatter extends TextInputFormatter {
final int maximumFractionDigits;
NumberInputFormatter({
this.maximumFractionDigits = 3,
}) : assert(maximumFractionDigits != null && maximumFractionDigits >= 0);
#override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
var newText = newValue.text;
var selectionOffset = newValue.selection.extent.offset;
bool isNegative = false;
if (newText.startsWith('-')) {
newText = newText.substring(1);
isNegative = true;
}
if (newText.isEmpty) {
return newValue;
}
if (newText.indexOf('.') != newText.lastIndexOf('.')) {
// inputted more than one dot.
return oldValue;
}
if (newText.startsWith('.') && maximumFractionDigits > 0) {
newText = '0$newText';
selectionOffset += 1;
}
while (newText.length > 1 && !newText.startsWith('0.') && newText.startsWith('0')) {
newText = newText.substring(1);
selectionOffset -= 1;
}
if (_decimalDigitsOf(newText) > maximumFractionDigits) {
// delete the extra digits.
newText = newText.substring(0, newText.indexOf('.') + 1 + maximumFractionDigits);
}
if (newValue.text.length == oldValue.text.length - 1 &&
oldValue.text.substring(newValue.selection.extentOffset, newValue.selection.extentOffset + 1) == ',') {
// in this case, user deleted the thousands separator, we should delete the digit number before the cursor.
newText = newText.replaceRange(newValue.selection.extentOffset - 1, newValue.selection.extentOffset, '');
selectionOffset -= 1;
}
if (newText.endsWith('.')) {
// in order to calculate the selection offset correctly, we delete the last decimal point first.
newText = newText.replaceRange(newText.length - 1, newText.length, '');
}
int lengthBeforeFormat = newText.length;
newText = _removeComma(newText);
if (double.tryParse(newText) == null) {
// invalid decimal number
return oldValue;
}
newText = _addComma(newText);
selectionOffset += newText.length - lengthBeforeFormat; // thousands separator newly added
if (maximumFractionDigits > 0 && newValue.text.endsWith('.')) {
// decimal point is at the last digit, we need to append it back.
newText = '$newText.';
}
if (isNegative) {
newText = '-$newText';
}
return TextEditingValue(
text: newText,
selection: TextSelection.collapsed(offset: min(selectionOffset, newText.length)),
);
}
static int _decimalDigitsOf(String text) {
var index = text?.indexOf('.') ?? -1;
return index == -1 ? 0 : text.length - index - 1;
}
static String _addComma(String text) {
StringBuffer sb = StringBuffer();
var pointIndex = text.indexOf('.');
String integerPart;
String decimalPart;
if (pointIndex >= 0) {
integerPart = text.substring(0, pointIndex);
decimalPart = text.substring(pointIndex);
} else {
integerPart = text;
decimalPart = '';
}
List<String> parts = [];
while (integerPart.length > 3) {
parts.add(integerPart.substring(integerPart.length - 3));
integerPart = integerPart.substring(0, integerPart.length - 3);
}
parts.add(integerPart);
sb.writeAll(parts.reversed, ',');
sb.write(decimalPart);
return sb.toString();
}
static String _removeComma(String text) {
return text.replaceAll(',', '');
}
}
Try using this:
FilteringTextInputFormatter(RegExp(r'(^[0-9]*(?:\.[0-9]{0,3})?$)'), allow: true),
Basically the regex will try to match 0 or more occurences of digits followed by optional decimal followed by upto 3 digits after decimal. You can modify it to use negative value also ^(?:\-)?[0-9]*(?:\.[0-9]{0,3})?$.
full code is here,
(update you can also change for data by cursor)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;
class DecimalChecker extends TextInputFormatter {
DecimalChecker({this.decimalRange = 3})
: assert(decimalRange == null || decimalRange > 0);
final int decimalRange;
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
String valueTxt = newValue.text;
TextSelection valueSet = newValue.selection;
var newlength = newValue.text.length;
var oldlength = oldValue.text.length;
if (oldlength < newlength) {
Pattern p = RegExp(r'(\d+\.?)|(\.?\d+)|(\.?)');
valueTxt = p
.allMatches(valueTxt)
.map<String>((Match match) => match.group(0))
.join();
print("------>");
if (valueTxt.startsWith('.')) {
valueTxt = '0.';
} else if (valueTxt.contains('.')) {
if (valueTxt.substring(valueTxt.indexOf('.') + 1).length >
decimalRange) {
valueTxt = oldValue.text;
} else {
if (valueTxt.split('.').length > 2) {
List<String> split = valueTxt.split('.');
valueTxt = split[0] + '.' + split[1];
}
}
}
valueSet = newValue.selection.copyWith(
baseOffset: math.min(valueTxt.length, valueTxt.length + 1),
extentOffset: math.min(valueTxt.length, valueTxt.length + 1),
);
return TextEditingValue(
text: valueTxt, selection: valueSet, composing: TextRange.empty);
} else {
return TextEditingValue(
text: valueTxt, selection: valueSet, composing: TextRange.empty);
}
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'My Decimal Check App'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController numberController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(10.0, 0.0, 10.0, 0.0),
child: TextField(
controller: numberController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [DecimalChecker()],
decoration: InputDecoration(
hintText: "Please enter Number",
),
),
),
],
),
),
);
}
}

How do I integrate a game in my flutter app?

I want to use this game https://github.com/yum650350/tissuebox in my Flutter project. I tried calling different parts of the main page but it wasn't coming up properly even though the game itself is working.
I want to integrate it in my iOS app and want to call it with a
function which opens a separate page where the user can play the game.
Is there a way I can do it?
So this is what I tried:
I called the method of the game in a separate screen
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstScreen(),
));
}
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Screen'),
),
body: Center(
child: RaisedButton(
color: Colors.red,
child: Text('Go to Second Screen'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
**???**
}));
},
),
),
);
}
}
But I don't know what to call in ?? this part since the game isn't wrapped in any class. This is the code of the game I want to call.
import 'package:shared_preferences/shared_preferences.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/gestures.dart';
import 'package:flame/sprite.dart';
import 'package:flame/flame.dart';
import 'package:flame/util.dart';
import 'package:flame/game.dart';
import 'dart:math';
tissuemain() async {
var util = Util();
await util.fullScreen();
await util.setOrientation(DeviceOrientation.portraitUp);
//loadimages
//tissuebox : 0,1,2,3,4,5,6
//background : b
//crown : c
//tissue : t
await Flame.images.loadAll(['b', '0', '1', '2', '3', '4', '5', '6', 't', 'c']);
audioLoad(c) async => (await Flame.audio.load(c)).path;
setAudio(a, s, v) async {
await a.setUrl(await audioLoad(s), isLocal: true);
a.setVolume(v);
}
//audios
//single drag : s.mp3
//double drag : s.mp3
//triple drag : s.mp3
//tick tock : tk.mp3
//game over : a.mp3
GameTable.setAudioList(GameTable.audioList1, await audioLoad('s.mp3'));
GameTable.setAudioList(GameTable.audioList2, await audioLoad('d.mp3'));
GameTable.setAudioList(GameTable.audioList3, await audioLoad('t.mp3'));
await setAudio(GameTable.tickTock, 'tk.mp3', 1.0);
await setAudio(GameTable.gameOver, 'a.mp3', .5);
var game = GameTable((await SharedPreferences.getInstance()).getInt('hs') ?? 0);
var hDrag = HorizontalDragGestureRecognizer();
var vDrag = VerticalDragGestureRecognizer();
hDrag.onUpdate = game.onDragUpdate;
hDrag.onStart = game.onDragStart;
hDrag.onEnd = game.onDragEnd;
vDrag.onUpdate = game.onDragUpdate;
vDrag.onStart = game.onDragStart;
vDrag.onEnd = game.onDragEnd;
runApp(game.widget);
util.addGestureRecognizer(hDrag);
util.addGestureRecognizer(vDrag);
}
enum Drag { tissue, box, none }
class GameTable extends Game {
//Audio
//These are workarounds for the ios memory leak
static var
tickTock = AudioPlayer(),
gameOver = AudioPlayer(),
audioList1 = [AudioPlayer(), AudioPlayer(), AudioPlayer()],
audioList2 = [AudioPlayer(), AudioPlayer()],
audioList3 = [AudioPlayer(), AudioPlayer()],
audioIndex1 = 0,
audioIndex2 = 0,
audioIndex3 = 0;
static getPlayIndex(int audioPlayer) {
if (audioPlayer == 1)
audioIndex1 = audioIndex1 < audioList1.length - 1 ? audioIndex1 + 1 : 0;
else if (audioPlayer == 2)
audioIndex2 = audioIndex2 < audioList2.length - 1 ? audioIndex2 + 1 : 0;
else if (audioPlayer == 3) audioIndex3 = audioIndex3 < audioList3.length - 1 ? audioIndex3 + 1 : 0;
return audioPlayer == 1 ? audioIndex1 : audioPlayer == 2 ? audioIndex2 : audioIndex3;
}
static get tissue1 => audioList1[getPlayIndex(1)];
static get tissue2 => audioList2[getPlayIndex(2)];
static get tissue3 => audioList3[getPlayIndex(3)];
static setAudioList(List<AudioPlayer> al,String audioName) => al.forEach((x) {
x.setUrl(audioName, isLocal: true);
x.setVolume(.2);
});
//
var background = Sprite('b'),
crown = Sprite('c'),
initialPoint = Offset.zero,
destPoint = Offset.zero,
dragState = Drag.none,
gameing = false,
gameover = false,
timePass = .0,
heighScore = 0,
score = 0,
timePassTemp = 0;
double tileSize, point1;
double get k => screenSize.width / 5 / tileSize;
Size screenSize;
Rect rect;
TissueBox tissueBox;
saveHighScore() async => await (await SharedPreferences.getInstance()).setInt('hs', heighScore);
GameTable(this.heighScore) {
init();
}
init() async {
resize(await Flame.util.initialDimensions());
rect = Rect.fromLTWH(.0, screenSize.height - tileSize * 23, tileSize * 9, tileSize * 23);
tissueBox = TissueBox(this);
}
#override
render(Canvas c) {
paintText(txt, offset, center, fontSize) {
var painter = TextPainter(
text: TextSpan(
style: TextStyle(
color: Colors.white,
fontSize: fontSize,
fontFamily: 'NS'),
text: txt),
textScaleFactor: k,
textDirection: TextDirection.ltr);
painter.layout();
painter.paint(c, center ? Offset(offset.dx - painter.width / 2, offset.dy) : offset);
}
background.renderRect(c, rect);
tissueBox.render(c);
var horCenter = tissueBox.initialLeft + tissueBox.boxRect.width / 2;
if (gameing)
paintText(timePass.toStringAsFixed(timePass < 1 ? 1 : 0) + 's', Offset(horCenter + 8, k * 23), true, k * 10);
var heighScoreTxt = heighScore.toString();
paintText(heighScoreTxt, Offset(heighScoreTxt.length==1?44.0:heighScoreTxt.length>2?22.0:33.0, k * 30), false, k * 12);
crown.renderRect(c, Rect.fromLTWH(28.0, k * 10, 49.2, 39.0));
paintText(score.toString(), Offset(horCenter, k * 50), true, k * 25);
heighScore = score > heighScore ? score : heighScore;
}
#override
update(double t) {
tissueBox.update(t);
timePass -= gameing || gameover ? t : 0;
if (timePass < 0 && gameing) {
tissueBox.isAway = true;
gameing = false;
timePass = 2;
gameover = true;
saveHighScore();
tissueBox.newGame();
} else if (gameing && !gameover) {
var floor = timePass.floor();
if (floor < timePassTemp && floor < 6 && floor != 0)
TissueBox.delay(Duration(milliseconds: 300), () => GameTable.tickTock.resume());
timePassTemp = floor;
}
gameover = timePass <= 0 && gameover ? false : gameover;
}
resize(s) {
screenSize = s;
tileSize = screenSize.width / 9;
}
onDragStart(DragStartDetails detail) {
var point = detail.globalPosition;
dragState = tissueBox.tissue.rect.contains(point) ? Drag.tissue : tissueBox.boxRect.contains(point) ? Drag.box : Drag.none;
initialPoint = Offset(point.dx == 0 ? initialPoint.dx : point.dx, point.dy == 0 ? initialPoint.dy : point.dy);
point1 = (tissueBox.tissue.rect.left - point.dx).abs();
}
onDragUpdate(DragUpdateDetails detail) {
if (gameover || dragState == Drag.none) return;
var point = detail.globalPosition;
destPoint = Offset(point.dx == 0 ? destPoint.dx : point.dx, point.dy == 0 ? destPoint.dy : point.dy);
if (dragState == Drag.tissue) {
if (initialPoint.dy - destPoint.dy > 100) {
if (gameing != true && gameover != true) {
gameing = true;
timePass = 10;
score = 0;
}
var sub = (point1 - (tissueBox.tissue.rect.left - point.dx).abs()).abs();
var addPoint = sub < 3 ? 3 : sub < 6 ? 2 : 1;
dragState = Drag.none;
tissueBox.nextTissue(addPoint);
playTissueAudio(addPoint);
score += addPoint;
}
} else if (dragState == Drag.box) {
tissueBox.boxRect = Rect.fromLTWH(tissueBox.initialLeft + destPoint.dx - initialPoint.dx, tissueBox.boxRect.top, TissueBox.boxSize.dx, TissueBox.boxSize.dy);
tissueBox.ismoving = true;
}
}
playTissueAudio(i) => (i == 1 ? GameTable.tissue1 : i == 2 ? GameTable.tissue2 : GameTable.tissue3).resume();
onDragEnd(DragEndDetails detail) {
initialPoint = Offset.zero;
dragState = Drag.none;
tissueBox.tissue.isMoving = false;
tissueBox.ismoving = false;
destPoint = initialPoint;
}
}
class TissueBox {
Rect get initialRect => Rect.fromLTWH(boxRect.center.dx - Tissue.width / 2, boxRect.top - boxRect.height + 20.3, Tissue.width, Tissue.width);
Sprite get getBoxSprite =>Sprite( rnd.nextInt(7).toString());
var tissueAwayList = List<TissueAway>(), rnd = Random(), ismoving = false, isAway = false;
Offset get getTissueUpPosition => Offset(initialRect.left, initialRect.top - 150);
final GameTable game;
Sprite boxSprite;
Rect boxRect;
int tissueCount;
Tissue tissue;
double get initialLeft => game.screenSize.width / 2 - TissueBox.boxSize.dx / 2;
double get initialTop => game.screenSize.height - game.tileSize * 5.5;
static var boxSize = Offset(150.0, 100.0);
TissueBox(this.game) {
boxRect = Rect.fromLTWH(initialLeft, initialTop, boxSize.dx, boxSize.dy);
tissueCount = 10 - rnd.nextInt(5);
tissue = Tissue(game, this);
boxSprite = getBoxSprite;
}
render(Canvas c) {
boxSprite.renderRect(c, boxRect);
tissue.render(c);
tissueAwayList.forEach((x) => x.render(c));
}
update(double t) {
tissue.update(t);
tissueAwayList.removeWhere((x) => x.isAway);
tissueAwayList.forEach((x) => x.update(t));
var distense = boxRect.left - initialLeft;
if (ismoving && !game.gameover) {
if (distense.abs() > 50 && tissueCount == 0){
isAway = true;
}
} else if (isAway && !game.gameover) {
boxRect = boxRect.shift(Offset(distense > 0 ? boxRect.left + game.k * 11 : boxRect.left - game.k * 11, boxRect.top));
if (boxRect.right < -50 || boxRect.left > game.screenSize.width + 50) {
newBox();
}
} else if (isAway && game.gameover) {
var target = Offset(boxRect.left, game.screenSize.height + Tissue.width) - Offset(boxRect.left, boxRect.top);
boxRect = boxRect.shift(
game.k * 11 < target.distance ?
Offset.fromDirection(target.direction, game.k * 11)
: target);
} else {
var target = Offset(initialLeft, initialTop) - Offset(boxRect.left, boxRect.top);
boxRect = boxRect.shift(
game.k * 11 < target.distance ?
Offset.fromDirection(target.direction, game.k * 11)
: target);
}
}
nextTissue(int pointsAdd) {
var duration = Duration(milliseconds: 100);
tissueAwayList.add(TissueAway(game, this));
if (pointsAdd > 1)
delay(duration, () {
tissueAwayList.add(TissueAway(game, this));
if (pointsAdd > 2)
delay(duration, () {
tissueAwayList.add(TissueAway(game, this));
});
});
tissue = Tissue(game, this, --tissueCount == 0);
}
newBox() {
boxSprite = getBoxSprite;
boxRect = Rect.fromLTWH(boxRect.right < -0 ? game.screenSize.width + 50 - boxSize.dx : -50.0, initialTop, boxSize.dx, boxSize.dy);
tissueCount = 10 - rnd.nextInt(5);
tissue = Tissue(game, this);
isAway = false;
ismoving = false;
}
newGame() async {
isAway = true;
GameTable.gameOver.resume();
await delay(Duration(seconds: 2), () {});
newBox();
}
static delay(duration, func()) async => await Future.delayed(duration, func);
}
class Tissue {
var tissueSprite = Sprite('t'), isMoving = false;
static var width = 100.0;
final TissueBox tissueBox;
final GameTable game;
bool isAway;
Rect rect;
Tissue(this.game, this.tissueBox, [this.isAway = false]) {
rect = tissueBox.initialRect;
}
render(Canvas c) => tissueSprite.renderRect(c, rect);
update(double t) => rect = isAway ? rect.shift(Offset.infinite) : tissueBox.initialRect;
}
class TissueAway extends Tissue {
TissueAway(GameTable game, TissueBox tissueBox) : super(game, tissueBox);
render(Canvas c) => tissueSprite.renderRect(c, rect);
update(double t) {
var speed = 500 * t;
Offset target = tissueBox.getTissueUpPosition - Offset(rect.left, rect.top);
if (speed < target.distance)
rect = rect.shift(Offset.fromDirection(target.direction, speed));
else
isAway = true;
}
}
The code in the main method will have a line that says runApp(...). The value of the ... is a widget that will be treated as the root widget of the app. In theory, you could just take that widget and pass it to your Navigator.push method and it should treat that widget as any other widget.
That being said, real life probably won't be as clean as this. There might be some initialization code in that app's main method or root widget that won't work properly if the app has long since already been initialized. Since virtually every Flutter app's root widget creates a WidgetsApp (or one of its derived classes MaterialApp or CupertinoApp), there might be some conflict that arrives from having one of those widgets being inserted as a descendent of another one of those widgets.
The game in question may work simply, or it might take some tweaking to work properly. That's something that will depend entirely on what app you are trying to embed into your own, so the only solution is to try it out and see for yourself.