Related
I am currently working on a pathfinding-visualizer-app and i am a bit overwhelmed by using a completely new programming language (at least for me).
But i think it is the best way to learn it as fast as possible.
To get to my problem:
I have a gridPaper with container Widgets. Every time i click on a container it changes the color from white to black.
So far so good...
For my pathfinding algorithm i need a 'start' and 'end' container (obviously a container where my algorithm starts searching for the end point).
Those i want to colorize in green (start) and red (end).
If i open the settings widget to click on "Start", it changes a "var = int" to 2 and then it goes to a switch case function.
From there if it gets a 1, it should print a black container, and if it gets a 2, it should fill the container green.
But then it starts to fill every container with the color green, because it goes through the complete offset ...
Do do have any ideas how to solve my problem ?
Code:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final appTitle = 'Path Finder';
final Color gridColor = Colors.lightBlue[100];
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends HookWidget {
final double cellSize = 20.0;
final String title;
var block = 1;
GlobalKey _key = GlobalKey();
MyHomePage({
Key key,
this.title,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final _activated = useState<List<Offset>>([]);
void _toggle(Offset offset) {
if (!_activated.value.remove(offset)) _activated.value.add(offset);
_activated.value = [..._activated.value];
}
return Scaffold(
appBar: AppBar(title: Text(title)),
body: GestureDetector(
onTapDown: (details) => _toggle(details.localPosition ~/ cellSize),
child: GridPaper(
child: Stack(
children: gridContainerMap(_activated),
),
color: Colors.lightBlue[100],
interval: cellSize,
divisions: 1,
subdivisions: 1,
),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Einstellungen'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Startpunkt'),
onTap: () {
// Update the state of the app
// ...
block = 2;
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Ziel'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Pathfinding-Algorithm'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
);
}
List<Widget> gridContainerMap(ValueNotifier<List<Offset>> _activated) {
switch (block) {
case 1:
return [
Container(color: Colors.white),
..._activated.value.map((offset) {
print('OFFSET: $offset');
return coloringWallContainer(offset);
}).toList(),
];
break;
case 2:
return [
Container(color: Colors.white),
..._activated.value.map((offset) {
print('OFFSET: $offset');
return coloringStartContainer(offset);
}).toList(),
];
break;
default:
}
}
Positioned coloringWallContainer(Offset offset) {
return Positioned(
left: offset.dx * cellSize,
top: offset.dy * cellSize,
width: cellSize,
height: cellSize,
child: ColoredBox(color: Colors.black),
);
}
Positioned coloringStartContainer(Offset offset) {
block = 1;
return Positioned(
left: offset.dx * cellSize,
top: offset.dy * cellSize,
width: cellSize,
height: cellSize,
child: ColoredBox(color: Colors.green),
);
}
}
Best regards
Robsen
Ah nevermind, i just needed to know where i get the Offset from the next grid element...
After that it was relatively easy.
Can anyone show how to make this?
You can use this code.
class _HomePageState extends State<HomePage> {
List<String> _list = List.generate(5, (i) => "${i}");
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ReorderableListView(
padding: EdgeInsets.symmetric(horizontal: 40),
children: _list.map((String string) => ListTile(key: Key(_list[_list.indexOf(string)]), title: Text(string))).toList(),
onReorder: (oldIndex, newIndex) {
String old = _list[oldIndex];
if (oldIndex > newIndex) {
for (int i = oldIndex; i > newIndex; i--) {
_list[i] = _list[i - 1];
}
_list[newIndex] = old;
} else {
for (int i = oldIndex; i < newIndex - 1; i++) {
_list[i] = _list[i + 1];
}
_list[newIndex - 1] = old;
}
setState(() {});
},
),
);
}
}
Another option, instead of directly adjusting index values:
if (oldIndex < newIndex) {
newIndex -= 1;
}
final int item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
As can be seen in an example on https://api.flutter.dev/flutter/material/ReorderableListView-class.html.
Was trying to do this myself today. Was not satisfied with either answer. Here is what I came up with.
ReorderableListView(
padding: EdgeInsets.all(6),
buildDefaultDragHandles: false,
onReorder: (int oldIndex, int newIndex) {
test.insert(newIndex, test[oldIndex]);
if (oldIndex < newIndex) {
test.removeAt(oldIndex);
} else {
test.removeAt(oldIndex + 1);
}
setState(() {});
},
children: test.asMap().entries.map((e) {
return ReorderableDragStartListener(
index: e.key,
key: Key(e.key.toString() + e.value),
child: AspectRatio(
aspectRatio: 16 / 9,
child: Card(
child: Center(child: Text(e.value)),
elevation: 4,
),
),
);
}).toList(),
),
It this avoids the unneeded loops in user6274128's answer, and doesn't have the bug in Scott's answer. It also avoids bugs with index being based on the List.indexof as that will return the first instance of the String instead of the correct one.
I'm trying to create a range slider on top of a Row of Containers which should create an audio waveform, but I have no idea where to even start...
The main issue is that the range slider sits right on top of the row of containers and it should change their colors on the "selected" section.
Here's what I currently have:
The code to create the image and details.
class BeatLyricsPage extends StatefulWidget {
final Beat beat;
BeatLyricsPage(this.beat);
#override
_BeatLyricsPageState createState() => _BeatLyricsPageState(beat);
}
class _BeatLyricsPageState extends State<BeatLyricsPage> {
final Beat beat;
final _kPicHeight = 190.0;
// used in _buildPageHeading to add the beat key and beat bpm
Widget _buildBeatInfoItem(String text) => DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: MyColor.white, width: 1.0),
borderRadius: BorderRadius.circular(4.0),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 3.0, horizontal: 12.0),
child: Text(text, style: TextStyle(color: MyColor.white, fontSize: 10.0, fontWeight: FontWeight.w600)),
),
);
final _kAudioControlsWidth = 180.0;
final _kAudioControlsHeight = 36.0;
final _kAudioControlsMainButtonSize = 56.0;
Widget _buildAudioControls(BuildContext context) => Positioned(
left: (MediaQuery.of(context).size.width / 2) - (_kAudioControlsWidth / 2),
top: _kPicHeight - (_kAudioControlsHeight / 2),
child: Stack(
overflow: Overflow.visible,
children: [
Container(
width: _kAudioControlsWidth,
height: _kAudioControlsHeight,
decoration: BoxDecoration(color: MyColor.darkGrey, borderRadius: BorderRadius.circular(100.0)),
padding: EdgeInsets.symmetric(horizontal: LayoutSpacing.sm),
child: Row(
children: [
CButtonLike(beatId: beat.id),
Spacer(),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: Icon(BeatPulseIcons.cart),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LicenseOptionsPage(beat))),
),
],
),
),
// ****** MAIN BUTTON (Play/Pause) ******
Positioned(
left: (_kAudioControlsWidth / 2) - (_kAudioControlsMainButtonSize / 2),
top: (_kAudioControlsHeight - _kAudioControlsMainButtonSize) / 2,
child: Container(
height: _kAudioControlsMainButtonSize,
width: _kAudioControlsMainButtonSize,
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topLeft, colors: [MyColor.primary, Color(0xFFf80d0a)]),
borderRadius: BorderRadius.circular(100.0)),
child: CButtonPlay(),
),
)
],
),
);
Widget _buildWaveForm() {
// creates a random list of doubles, "fake data"
var rng = Random();
final List waveFormData = [];
for (var i = 0; i < 90; i++) {
waveFormData.add(rng.nextInt(45).toDouble());
}
// player bloc
final playerBloc = BlocProvider.getPlayerBloc(context);
// renders
return Container(
height: _kPicHeight,
padding: EdgeInsets.symmetric(vertical: LayoutSpacing.xxxl),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// current playing second
StreamBuilder<double>(
stream: playerBloc.playingSecond,
initialData: 0.0,
builder: (_, playingSecondSnapshot) {
// current beat playing
return StreamBuilder<Beat>(
stream: playerBloc.playingBeat,
builder: (_, playingBeatSnapshot) {
final playingBeat = playingBeatSnapshot.data;
// if the beat playing is the same as the beat selected for the lyrics, show playing seconds
if (playingBeat?.id == beat.id)
return Text(secondsToTime(playingSecondSnapshot.data), style: MyFontStyle.sizeXxs);
// otherwise show 0:00
else
return Text(secondsToTime(0), style: MyFontStyle.sizeXxs);
},
);
},
),
SizedBox(width: LayoutSpacing.xs),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: waveFormData
.map((waveFormDataIndex) => Container(
height: waveFormDataIndex > 5.0 ? waveFormDataIndex : 5.0,
width: 2,
color: MyColor.white,
margin: EdgeInsets.only(right: 1),
))
.toList(),
),
SizedBox(width: LayoutSpacing.xs),
Text(secondsToTime(beat.length), style: MyFontStyle.sizeXxs),
],
),
);
}
Widget _buildPageHeading(BuildContext context, {#required String imageUrl}) => Stack(
children: [
Column(
children: [
Hero(
tag: MyKeys.makePlayerCoverKey(beat.id),
child: Opacity(
opacity: 0.3,
child: Container(
height: _kPicHeight,
decoration: BoxDecoration(
image: DecorationImage(image: CachedNetworkImageProvider(imageUrl), fit: BoxFit.cover),
),
),
),
),
Container(color: MyColor.background, height: LayoutSpacing.xl)
],
),
Padding(
padding: EdgeInsets.all(LayoutSpacing.xs),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_buildBeatInfoItem(beat.key),
SizedBox(width: 4.0),
_buildBeatInfoItem('${beat.bpm} BPM'),
],
),
),
_buildAudioControls(context),
_buildWaveForm(),
],
);
}
To create a custom range slider, you can use the GestureRecognizer and save the position of each slider in variable inside a StatefulWidget. To decide wether a bar with the index i is inside the range, you can divide the pixel position of the limiter(bar1&bar2 in the source below) by the width of bars and compare it to i.
Sadly I couldn't work with your code example. Instead I created a bare minimum example as you can see below. If you take a minute to read into, I'm sure you can transfer it to your application.
import 'dart:math';
import 'package:flutter/material.dart';
List<int> bars = [];
void main() {
// generate random bars
Random r = Random();
for (var i = 0; i < 50; i++) {
bars.add(r.nextInt(200));
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> {
static const barWidth = 5.0;
double bar1Position = 60.0;
double bar2Position = 180.0;
#override
Widget build(BuildContext context) {
int i = 0;
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.centerLeft,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: bars.map((int height) {
Color color =
i >= bar1Position / barWidth && i <= bar2Position / barWidth
? Colors.deepPurple
: Colors.blueGrey;
i++;
return Container(
color: color,
height: height.toDouble(),
width: 5.0,
);
}).toList(),
),
Bar(
position: bar2Position,
callback: (DragUpdateDetails details) {
setState(() {
bar2Position += details.delta.dx;
});
},
),
Bar(
position: bar1Position,
callback: (DragUpdateDetails details) {
setState(() {
bar1Position += details.delta.dx;
});
},
),
],
),
),
);
}
}
class Bar extends StatelessWidget {
final double position;
final GestureDragUpdateCallback callback;
Bar({this.position, this.callback});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: position >= 0.0 ? position : 0.0),
child: GestureDetector(
onHorizontalDragUpdate: callback,
child: Container(
color: Colors.red,
height: 200.0,
width: 5.0,
),
),
);
}
}
in order to have a wave slider :
class WaveSlider extends StatefulWidget {
final double initialBarPosition;
final double barWidth;
final int maxBarHight;
final double width;
WaveSlider({
this.initialBarPosition = 0.0,
this.barWidth = 5.0,
this.maxBarHight = 50,
this.width = 60.0,
});
#override
State<StatefulWidget> createState() => WaveSliderState();
}
class WaveSliderState extends State<WaveSlider> {
List<int> bars = [];
double barPosition;
double barWidth;
int maxBarHight;
double width;
int numberOfBars;
void randomNumberGenerator() {
Random r = Random();
for (var i = 0; i < numberOfBars; i++) {
bars.add(r.nextInt(maxBarHight - 10) + 10);
}
}
_onTapDown(TapDownDetails details) {
var x = details.globalPosition.dx;
print("tap down " + x.toString());
setState(() {
barPosition = x;
});
}
#override
void initState() {
super.initState();
barPosition = widget.initialBarPosition;
barWidth = widget.barWidth;
maxBarHight = widget.maxBarHight.toInt();
width = widget.width;
if (bars.isNotEmpty) bars = [];
numberOfBars = width ~/ barWidth;
randomNumberGenerator();
}
#override
Widget build(BuildContext context) {
int barItem = 0;
return Scaffold(
backgroundColor: Colors.grey[900],
body: Center(
child: GestureDetector(
onTapDown: (TapDownDetails details) => _onTapDown(details),
onHorizontalDragUpdate: (DragUpdateDetails details) {
setState(() {
barPosition = details.globalPosition.dx;
});
},
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: bars.map((int height) {
Color color = barItem + 1 < barPosition / barWidth
? Colors.white
: Colors.grey[600];
barItem++;
return Row(
children: <Widget>[
Container(
width: .1,
height: height.toDouble(),
color: Colors.black,
),
Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(1.0),
topRight: const Radius.circular(1.0),
),
),
height: height.toDouble(),
width: 4.8,
),
Container(
width: .1,
height: height.toDouble(),
color: Colors.black,
),
],
);
}).toList(),
),
),
),
),
);
}
}
and use it like :
WaveSlider(
initialBarPosition: 180.0,
barWidth: 5.0,
maxBarHight: 50,
width: MediaQuery.of(context).size.width,
)
I want to nest PageViews in flutter, with a PageView inside a Scaffold inside a PageView. In the outer PageView I will have the logo and contact informations, as well as secundary infos. As a child, I will have a scaffold with the inner PageView and a BottomNavigationBar as the main user interaction screen. Here is the code I have so far:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp>{
int index = 0;
final PageController pageController = PageController();
final Curve _curve = Curves.ease;
final Duration _duration = Duration(milliseconds: 300);
_navigateToPage(value){
pageController.animateToPage(
value,
duration: _duration,
curve: _curve
);
setState((){
index = value;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageViewCeption',
home: PageView(
children: <Widget>[
Container(
color: Colors.blue,
),
Scaffold(
body: PageView(
controller: pageController,
onPageChanged: (page){
setState(() {
index = page;
});
},
children: <Widget>[
Container(
child: Center(
child: Text('1', style: TextStyle(color: Colors.white))
)
),
Container(
child: Center(
child: Text('2', style: TextStyle(color: Colors.white))
)
),
Container(
child: Center(
child: Text('3', style: TextStyle(color: Colors.white))
)
),
],
),
backgroundColor: Colors.green,
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (value) =>_navigateToPage(value),
currentIndex: index,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.cake),
title: Text('1')
),
BottomNavigationBarItem(
icon: Icon(Icons.cake),
title: Text('2')
),
BottomNavigationBarItem(
icon: Icon(Icons.cake),
title: Text('3')
)
],
),
),
Container(
color: Colors.blue
)
],
),
);
}
}
Here is the result:
Problem is: When I am in the inner PageView, I can't get away from it to the outer one scrolling left on the first page, or scrolling right on the last page of the inner PageView. The only way to go back to the outer PageView in scrolling (swiping) on the BottomNavigationBar.
In the docs of the Scroll Physics Class we find this in the description:
For example, determines how the Scrollable will behave when the user reaches the maximum scroll extent or when the user stops scrolling.
But I haven't been able to come up with a solution yet. Any thoughts?
Update 1
I had progress working with a CustomScrollPhysics class:
class CustomScrollPhysics extends ScrollPhysics{
final PageController _controller;
const CustomScrollPhysics(this._controller, {ScrollPhysics parent }) : super(parent: parent);
#override
CustomScrollPhysics applyTo(ScrollPhysics ancestor) {
return CustomScrollPhysics(_controller, parent: buildParent(ancestor));
}
#override
double applyBoundaryConditions(ScrollMetrics position, double value) {
assert(() {
if (value == position.pixels) {
throw new FlutterError(
'$runtimeType.applyBoundaryConditions() was called redundantly.\n'
'The proposed new position, $value, is exactly equal to the current position of the '
'given ${position.runtimeType}, ${position.pixels}.\n'
'The applyBoundaryConditions method should only be called when the value is '
'going to actually change the pixels, otherwise it is redundant.\n'
'The physics object in question was:\n'
' $this\n'
'The position object in question was:\n'
' $position\n'
);
}
return true;
}());
if (value < position.pixels && position.pixels <= position.minScrollExtent){ // underscroll
_controller.jumpTo(position.viewportDimension + value);
return 0.0;
}
if (position.maxScrollExtent <= position.pixels && position.pixels < value) {// overscroll
_controller.jumpTo(position.viewportDimension + (value - position.viewportDimension*2));
return 0.0;
}
if (value < position.minScrollExtent && position.minScrollExtent < position.pixels) // hit top edge
return value - position.minScrollExtent;
if (position.pixels < position.maxScrollExtent && position.maxScrollExtent < value) // hit bottom edge
return value - position.maxScrollExtent;
return 0.0;
}
}
Which is a modification of the ClampingScrollPhysics applyBoundaryConditions. It kinda works but because of the pageSnapping it is really buggy. It happens, because according to the docs:
Any active animation is canceled. If the user is currently scrolling, that
action is canceled.
When the action is canceled, the PageView starts to snap back to the Scafold page, if the user stop draggin the screen, and this messes things up. Any ideas on how to avoid the page snapping in this case, or for better implementation for that matter?
I was able to replicate the issue on the nested PageView. It seems that the inner PageView overrides the detected gestures. This explains why we're unable to navigate to other pages of the outer PageView, but the BottomNavigationBar can. More details of this behavior is explained in this thread.
As a workaround, you can use a single PageView and just hide the BottomNavigationBar on the outer pages. I've modified your code a bit.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
var index = 0;
final PageController pageController = PageController();
final Curve _curve = Curves.ease;
final Duration _duration = Duration(milliseconds: 300);
var isBottomBarVisible = false;
_navigateToPage(value) {
// When BottomNavigationBar button is clicked, navigate to assigned page
switch (value) {
case 0:
value = 1;
break;
case 1:
value = 2;
break;
case 2:
value = 3;
break;
}
pageController.animateToPage(value, duration: _duration, curve: _curve);
setState(() {
index = value;
});
}
// Set BottomNavigationBar indicator only on pages allowed
_getNavBarIndex(index) {
if (index <= 1)
return 0;
else if (index == 2)
return 1;
else if (index >= 3)
return 2;
else
return 0;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageViewCeption',
home: Scaffold(
body: Container(
child: PageView(
controller: pageController,
onPageChanged: (page) {
setState(() {
// BottomNavigationBar only appears on page 1 to 3
isBottomBarVisible = page > 0 && page < 4;
print('page: $page bottom bar: $isBottomBarVisible');
index = page;
});
},
children: <Widget>[
Container(
color: Colors.red,
),
Container(
color: Colors.orange,
),
Container(
color: Colors.yellow,
),
Container(
color: Colors.green,
),
Container(color: Colors.lightBlue)
],
),
),
bottomNavigationBar: isBottomBarVisible // if true, generate BottomNavigationBar
? new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: (value) => _navigateToPage(value),
currentIndex: _getNavBarIndex(index),
items: [
BottomNavigationBarItem(icon: Icon(Icons.cake), label: '1'),
BottomNavigationBarItem(icon: Icon(Icons.cake), label: '2'),
BottomNavigationBarItem(icon: Icon(Icons.cake), label: '3')
],
)
//else, create an empty container to hide the BottomNavigationBar
: Container(
height: 0,
),
),
);
}
}
Following is the image of Game I am trying to build using flutter for android:
Male body with tags naming body parts
Following is the code I have written and right now it's hardcoded for the Google Pixel, for portrait mode
Map _decoded;
List oldData=[];
int i=0;
class IdentifyGame extends StatefulWidget {
Function onScore;
Function onProgress;
Function onEnd;
int iteration;
bool isRotated;
IdentifyGame(
{key,
this.onScore,
this.onProgress,
this.onEnd,
this.iteration,
this.isRotated = false})
: super(key: key);
#override
State createState() => new _IdentifyGameState();
}
class _IdentifyGameState extends State
with SingleTickerProviderStateMixin {
double x1 = 0.0,
y1 = 0.0,
x2 = 0.0,
y2 = 0.0,
x3 = 0.0,
y3 = 0.0,
x4 = 0.0,
y4 = 0.0,
x5 = 0.0,
y5 = 0.0,
x6 = 0.0,
y6 = 0.0,
String paste1 = '',
paste2 = '',
paste3 = '',
paste4 = '',
paste5 = '',
paste6 = '',
AnimationController _imgController;
Animation animateImage;
Future _loadGameAsset() async {
return await rootBundle.loadString("assets/imageCoordinatesInfoBody.json");
}
Future _loadGameInfo() async {
String jsonGameInfo = await _loadGameAsset();
print(jsonGameInfo);
this.setState(() {
_decoded = json.decode(jsonGameInfo);
});
print((_decoded["parts"] as List));
}
#override
void initState() {
super.initState();
this._loadGameInfo();
_imgController = new AnimationController(
duration: new Duration(
milliseconds: 800,
),
vsync: this);
animateImage =
new CurvedAnimation(parent: _imgController, curve: Curves.bounceInOut);
_imgController.forward();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
}
_renderChoice1(String text, double X, double Y) {
setState(() {
paste1 = text;
x1 = X;
y1 = Y;
});
}
_renderChoice2(String text, double X, double Y) {
setState(() {
paste2 = text;
x2 = X;
y2 = Y;
});
}
_renderChoice3(String text, double X, double Y) {
setState(() {
paste3 = text;
x3 = X;
y3 = Y;
});
}
_renderChoice4(String text, double X, double Y) {
setState(() {
paste4 = text;
x4 = X;
y4 = Y;
});
}
_renderChoice5(String text, double X, double Y) {
setState(() {
paste5 = text;
x5 = X;
y5 = Y;
});
}
_renderChoice6(String text, double X, double Y) {
setState(() {
paste6 = text;
x6 = X;
y6 = Y;
});
}
Widget _builtButton(BuildContext context, double maxHeight, double maxWidth) {
print((_decoded["parts"] as List).length);
int j = 0;
int r = ((_decoded["parts"] as List).length / 5 +
((((_decoded["parts"] as List).length % 5) == 0) ? 0 : 1))
.toInt();
print(r);
return new ResponsiveGridView(
rows: r,
cols: 5,
children: (_decoded["parts"] as List)
.map((e) => _buildItems(j++, e, maxHeight, maxWidth))
.toList(growable: false),
);
}
List _buildPartsList() {
List partsName = [];
for (var i = 0; i (i),
part: part,
);
}
_onDragStart(BuildContext context, DragStartDetails start) {
print(start.globalPosition.toString());
}
_onDragUpdate(BuildContext context, DragUpdateDetails update) {
print(update.globalPosition.toString());
}
#override
void dispose() {
_imgController.dispose();
SystemChrome.setPreferredOrientations([]);
super.dispose();
}
#override
Widget build(BuildContext context) {
print(_buildPartsList());
Size media = MediaQuery.of(context).size;
double _height = media.height;
double _width = media.width;
print("height of screen from media is $_height ");
print("width of screen from media is $_width");
return new LayoutBuilder(builder: (context, constraint) {
return new Scaffold(
body: new Flex(
direction: Axis.vertical,
children: [
new GestureDetector(
onHorizontalDragStart: (DragStartDetails start) =>
_onDragStart(context, start),
onHorizontalDragUpdate: (DragUpdateDetails update) =>
_onDragUpdate(context, update),
onVerticalDragStart: (DragStartDetails start) =>
_onDragStart(context, start),
onVerticalDragUpdate: (DragUpdateDetails update) =>
_onDragUpdate(context, update),
child: new Container(
height: (constraint.maxHeight) * 3 / 4,
width: constraint.maxWidth,
decoration: new BoxDecoration(
color: Theme.of(context).backgroundColor,
),
child: new Stack(
fit: StackFit.passthrough,
children: [
new ScaleTransition(
scale: animateImage,
child: new Image(
image: AssetImage('assets/' + _decoded["id"]),
fit: BoxFit.contain,
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste1,
x: x1,
y: y1,
height: _height,
width: _width),
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste2,
x: x2,
y: y2,
height: _height,
width: _width),
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste3,
x: x3,
y: y3,
height: _height,
width: _width),
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste4,
x: x4,
y: y4,
height: _height,
width: _width),
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste5,
x: x5,
y: y5,
height: _height,
width: _width),
),
),
new RepaintBoundary(
child: new CustomPaint(
painter: new Stickers(
text: paste6,
x: x6,
y: y6,
height: _height,
width: _width),
),
),
],
)),
),
new Container(
height: (constraint.maxHeight) / 4,
width: constraint.maxWidth,
decoration: new BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor),
child: _builtButton(
context, constraint.maxHeight, constraint.maxWidth)),
],
),
);
});
}
}
class DragBox extends StatefulWidget {
var part;
double maxHeight;
double maxWidth;
Function render1;
Function render2;
Function render3;
Function render4;
Function render5;
Function render6;
Function onScore;
Function onEnd;
DragBox(
{this.onEnd,
this.onScore,
this.maxHeight,
this.maxWidth,
Key key,
this.part,
this.render1,
this.render2,
this.render3,
this.render4,
this.render5,
this.render6,})
: super(key: key);
#override
DragBoxState createState() => new DragBoxState();
}
class DragBoxState extends State with TickerProviderStateMixin {
AnimationController controller, shakeController;
Animation animation, shakeAnimation, noanimation;
// Color draggableColor;
// String draggableText;
var part;
int _flag = 0;
double maxHeight;
double maxWidth;
Function render1;
Function render2;
Function render3;
Function render4;
Function render5;
Function render6;
static List _buildPartsList() {
List partsName = [];
for (var i = 0; i data = _buildPartsList();
_onDragStart(BuildContext context, DragStartDetails start) {
RenderBox getBox = context.findRenderObject();
var local = getBox.globalToLocal(start.globalPosition);
print(local.dx.toString() + "|" + local.dy.toString());
}
_onDragUpdate(BuildContext context, DragUpdateDetails update) {
RenderBox getBox = context.findRenderObject();
var local = getBox.globalToLocal(update.globalPosition);
print(local.dx.toString() + "|" + local.dy.toString());
}
void toAnimateFunction() {
animation.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
void toAnimateButton() {
shakeController.forward();
}
#override
void initState() {
super.initState();
shakeController = new AnimationController(
duration: new Duration(milliseconds: 800), vsync: this);
controller = new AnimationController(
duration: new Duration(milliseconds: 80), vsync: this);
animation = new Tween(begin: -3.0, end: 3.0).animate(controller);
animation.addListener(() {
setState(() {});
});
shakeAnimation =
new CurvedAnimation(parent: shakeController, curve: Curves.easeOut);
noanimation = new Tween(begin: 0.0, end: 0.0).animate(shakeController);
part = widget.part;
maxHeight = widget.maxHeight;
maxWidth = widget.maxWidth;
render1 = widget.render1;
render2 = widget.render2;
render3 = widget.render3;
render4 = widget.render4;
render5 = widget.render5;
render6 = widget.render6;
toAnimateButton();
}
#override
void dispose() {
controller.dispose();
shakeController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size media = MediaQuery.of(context).size;
double _height = media.height;
double _width = media.width;
return new ScaleTransition(
scale: shakeAnimation,
child: new GestureDetector(
onHorizontalDragStart: (DragStartDetails start) =>
_onDragStart(context, start),
onHorizontalDragUpdate: (DragUpdateDetails update) =>
_onDragUpdate(context, update),
onVerticalDragStart: (DragStartDetails start) =>
_onDragStart(context, start),
onVerticalDragUpdate: (DragUpdateDetails update) =>
_onDragUpdate(context, update),
child: new Draggable(
data: part["name"],
child: new AnimatedDrag(
animation: (_flag == 0) ? noanimation : animation,
draggableColor: Theme.of(context).buttonColor,
draggableText: part["name"]),
feedback: new AnimatedFeedback(
animation: animation,
draggableColor: Theme.of(context).disabledColor,
draggableText: part["name"]),
onDraggableCanceled: (velocity, offset) {
double hRatio = ((3 * maxHeight) / (4 * part["data"]["height"]));
double wRatio = (maxWidth / part["data"]["width"]);
if (part["name"] == "face" &&
(offset.dx > 360.0 && offset.dx 140.0 && offset.dy 336.0 && offset.dx 250.0 && offset.dy 260.0 && offset.dx 300.0 && offset.dy 430.0 && offset.dx 300.0 && offset.dy 392.0 && offset.dx 620.0 && offset.dy 220.0 && offset.dx 620.0 && offset.dy animation,
this.draggableColor,
this.draggableText})
: super(key: key, listenable: animation);
final Color draggableColor;
final String draggableText;
Widget build(BuildContext context) {
Size media = MediaQuery.of(context).size;
double _height = media.height;
double _width = media.width;
final Animation animation = listenable;
return new Container(
// width: _width * 0.15,
// height: _height * 0.06,
padding: new EdgeInsets.all(
(_width > _height) ? _width * 0.01 : _width * 0.015),
color: draggableColor.withOpacity(0.5),
child: new Center(
child: new Text(
draggableText,
style: new TextStyle(
color: Theme.of(context).highlightColor,
decoration: TextDecoration.none,
fontSize: (_width > _height) ? _width * 0.015 : _width * 0.03,
),
),
),
);
}
}
class AnimatedDrag extends AnimatedWidget {
AnimatedDrag(
{Key key,
Animation animation,
this.draggableColor,
this.draggableText})
: super(key: key, listenable: animation);
final Color draggableColor;
final String draggableText;
Widget build(BuildContext context) {
Size media = MediaQuery.of(context).size;
double _height = media.height;
double _width = media.width;
final Animation animation = listenable;
double translateX = animation.value;
print("value: $translateX");
return new Transform(
transform: new Matrix4.translationValues(translateX, 0.0, 0.0),
child: new Container(
// width: _width * 0.15,
// height: _height * 0.06,
padding: new EdgeInsets.all(
(_width > _height) ? _width * 0.01 : _width * 0.015),
margin: new EdgeInsets.symmetric(
vertical: (_width > _height) ? _width * 0.005 : _width * 0.005,
horizontal: (_width > _height) ? _width * 0.005 : _width * 0.005,
),
color: draggableColor,
child: new Center(
child: new Text(
draggableText,
style: new TextStyle(
color: Theme.of(context).hintColor,
decoration: TextDecoration.none,
fontSize: (_width > _height) ? _width * 0.015 : _width * 0.03,
),
),
),
),
);
}
}
class Stickers extends CustomPainter {
Stickers({this.text, this.x, this.y, this.height, this.width});
final String text;
final double x;
final double y;
final double width;
final double height;
#override
void paint(Canvas canvas, Size size) {
TextSpan span = new TextSpan(
text: text,
style: new TextStyle(
color: Colors.black,
fontSize: (width > height) ? width * 0.015 : width * 0.03,
fontWeight: FontWeight.bold));
TextPainter tp = new TextPainter(
text: span,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr);
tp.layout();
tp.paint(canvas, new Offset(x, y));
//canvas.restore();
canvas.save();
// canvas.saveLayer(rect, new Paint());
}
#override
bool shouldRepaint(Stickers oldDelegate) {
// TODO: implement shouldRepaint
if (oldDelegate.text != text) {
return true;
} else {
return false;
}
}
}
I am new to flutter and building a game for dragging and dropping the tag with the name of a body part on it to the correct location on the shown image, images will come from the database and it will change after successful completion and submission. Now here I am struggling with making it responsive for every image that will get loaded from the database. Right now it's hardcoded for the shown image. Any help would be highly appreciated. Thanks!