Flutter Flare, Rive, is it able to use for background? - flutter

I just succeed flutter-flare for my flutter widgets
but I could not find a example to use it for background (Container)
is this possible?

Use official example code https://github.com/2d-inc/Flare-Flutter/tree/stable/example/teddy/lib
with a little modification, you can use Stack and Positioned and change attribute value like top
code snippet
return Scaffold(
backgroundColor: Color.fromRGBO(93, 142, 155, 1.0),
body: Container(
child: Stack(
children: <Widget>[
Positioned(
top: 50,
left:0,
right: 0,
child: Container(
height: 200,
padding:
const EdgeInsets.only(left: 30.0, right: 30.0),
child: FlareActor(
"assets/Teddy.flr",
shouldClip: false,
alignment: Alignment.bottomCenter,
fit: BoxFit.contain,
controller: _teddyController,
)),),
Positioned(
child: SingleChildScrollView(
working demo
full code
import 'package:flutter/material.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/rendering.dart';
import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:flare_flutter/flare.dart';
import 'package:flare_dart/math/mat2d.dart';
import 'package:flare_dart/math/vec2d.dart';
import 'package:flare_flutter/flare_controls.dart';
// Adapted these helpful functions from:
// https://github.com/flutter/flutter/blob/master/packages/flutter/test/material/text_field_test.dart
// Returns first render editable
RenderEditable findRenderEditable(RenderObject root) {
RenderEditable renderEditable;
void recursiveFinder(RenderObject child) {
if (child is RenderEditable) {
renderEditable = child;
return;
}
child.visitChildren(recursiveFinder);
}
root.visitChildren(recursiveFinder);
return renderEditable;
}
List<TextSelectionPoint> globalize(
Iterable<TextSelectionPoint> points, RenderBox box) {
return points.map<TextSelectionPoint>((TextSelectionPoint point) {
return TextSelectionPoint(
box.localToGlobal(point.point),
point.direction,
);
}).toList();
}
Offset getCaretPosition(RenderBox box) {
final RenderEditable renderEditable = findRenderEditable(box);
if (!renderEditable.hasFocus) {
return null;
}
final List<TextSelectionPoint> endpoints = globalize(
renderEditable.getEndpointsForSelection(renderEditable.selection),
renderEditable,
);
return endpoints[0].point + const Offset(0.0, -2.0);
}
class SigninButton extends StatelessWidget {
final Widget child;
final Gradient gradient;
final double width;
final double height;
final Function onPressed;
const SigninButton({
Key key,
#required this.child,
this.gradient,
this.width = double.infinity,
this.height = 50.0,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: width,
height: 50.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
gradient: LinearGradient(
colors: <Color>[
Color.fromRGBO(160, 92, 147, 1.0),
Color.fromRGBO(115, 82, 135, 1.0)
],
)),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
child: Center(
child: child,
)),
),
);
}
}
class TeddyController extends FlareControls {
// Store a reference to our face control node (the "ctrl_look" node in Flare)
ActorNode _faceControl;
// Storage for our matrix to get global Flutter coordinates into Flare world coordinates.
Mat2D _globalToFlareWorld = Mat2D();
// Caret in Flutter global coordinates.
Vec2D _caretGlobal = Vec2D();
// Caret in Flare world coordinates.
Vec2D _caretWorld = Vec2D();
// Store the origin in both world and local transform spaces.
Vec2D _faceOrigin = Vec2D();
Vec2D _faceOriginLocal = Vec2D();
bool _hasFocus = false;
// Project gaze forward by this many pixels.
static const double _projectGaze = 60.0;
String _password;
#override
bool advance(FlutterActorArtboard artboard, double elapsed) {
super.advance(artboard, elapsed);
Vec2D targetTranslation;
if (_hasFocus) {
// Get caret in Flare world space.
Vec2D.transformMat2D(_caretWorld, _caretGlobal, _globalToFlareWorld);
// To make it more interesting, we'll also add a sinusoidal vertical offset.
_caretWorld[1] +=
sin(new DateTime.now().millisecondsSinceEpoch / 300.0) * 70.0;
// Compute direction vector.
Vec2D toCaret = Vec2D.subtract(Vec2D(), _caretWorld, _faceOrigin);
Vec2D.normalize(toCaret, toCaret);
Vec2D.scale(toCaret, toCaret, _projectGaze);
// Compute the transform that gets us in face "ctrl_face" space.
Mat2D toFaceTransform = Mat2D();
if (Mat2D.invert(toFaceTransform, _faceControl.parent.worldTransform)) {
// Put toCaret in local space, note we're using a direction vector
// not a translation so transform without translation
Vec2D.transformMat2(toCaret, toCaret, toFaceTransform);
// Our final "ctrl_face" position is the original face translation plus this direction vector
targetTranslation = Vec2D.add(Vec2D(), toCaret, _faceOriginLocal);
}
} else {
targetTranslation = Vec2D.clone(_faceOriginLocal);
}
// We could just set _faceControl.translation to targetTranslation, but we want to animate it smoothly to this target
// so we interpolate towards it by a factor of elapsed time in order to maintain speed regardless of frame rate.
Vec2D diff =
Vec2D.subtract(Vec2D(), targetTranslation, _faceControl.translation);
Vec2D frameTranslation = Vec2D.add(Vec2D(), _faceControl.translation,
Vec2D.scale(diff, diff, min(1.0, elapsed * 5.0)));
_faceControl.translation = frameTranslation;
return true;
}
// Fetch references for the `ctrl_face` node and store a copy of its original translation.
#override
void initialize(FlutterActorArtboard artboard) {
super.initialize(artboard);
_faceControl = artboard.getNode("ctrl_face");
if (_faceControl != null) {
_faceControl.getWorldTranslation(_faceOrigin);
Vec2D.copy(_faceOriginLocal, _faceControl.translation);
}
play("idle");
}
onCompleted(String name) {
play("idle");
}
// Called by [FlareActor] when the view transform changes.
// Updates the matrix that transforms Global-Flutter-coordinates into Flare-World-coordinates.
#override
void setViewTransform(Mat2D viewTransform) {
Mat2D.invert(_globalToFlareWorld, viewTransform);
}
// Transform the [Offset] into a [Vec2D].
// If no caret is provided, lower the [_hasFocus] flag.
void lookAt(Offset caret) {
if (caret == null) {
_hasFocus = false;
return;
}
_caretGlobal[0] = caret.dx;
_caretGlobal[1] = caret.dy;
_hasFocus = true;
}
void setPassword(String value) {
_password = value;
}
bool _isCoveringEyes = false;
coverEyes(cover) {
if (_isCoveringEyes == cover) {
return;
}
_isCoveringEyes = cover;
if (cover) {
play("hands_up");
} else {
play("hands_down");
}
}
void submitPassword() {
if (_password == "bears") {
play("success");
} else {
play("fail");
}
}
}
typedef void CaretMoved(Offset globalCaretPosition);
typedef void TextChanged(String text);
// Helper widget to track caret position.
class TrackingTextInput extends StatefulWidget {
TrackingTextInput(
{Key key, this.onCaretMoved, this.onTextChanged, this.hint, this.label, this.isObscured = false})
: super(key: key);
final CaretMoved onCaretMoved;
final TextChanged onTextChanged;
final String hint;
final String label;
final bool isObscured;
#override
_TrackingTextInputState createState() => _TrackingTextInputState();
}
class _TrackingTextInputState extends State<TrackingTextInput> {
final GlobalKey _fieldKey = GlobalKey();
final TextEditingController _textController = TextEditingController();
Timer _debounceTimer;
#override
initState() {
_textController.addListener(() {
// We debounce the listener as sometimes the caret position is updated after the listener
// this assures us we get an accurate caret position.
if (_debounceTimer?.isActive ?? false) _debounceTimer.cancel();
_debounceTimer = Timer(const Duration(milliseconds: 100), () {
if (_fieldKey.currentContext != null) {
// Find the render editable in the field.
final RenderObject fieldBox =
_fieldKey.currentContext.findRenderObject();
Offset caretPosition = getCaretPosition(fieldBox);
if (widget.onCaretMoved != null) {
widget.onCaretMoved(caretPosition);
}
}
});
if (widget.onTextChanged != null) {
widget.onTextChanged(_textController.text);
}
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: TextFormField(
decoration: InputDecoration(
hintText: widget.hint,
labelText: widget.label,
),
key: _fieldKey,
controller: _textController,
obscureText: widget.isObscured,
validator: (value) {}),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TeddyController _teddyController;
#override
initState() {
_teddyController = TeddyController();
super.initState();
}
#override
Widget build(BuildContext context) {
EdgeInsets devicePadding = MediaQuery.of(context).padding;
return Scaffold(
backgroundColor: Color.fromRGBO(93, 142, 155, 1.0),
body: Container(
child: Stack(
children: <Widget>[
Positioned(
top: 50,
left:0,
right: 0,
child: Container(
height: 200,
padding:
const EdgeInsets.only(left: 30.0, right: 30.0),
child: FlareActor(
"assets/Teddy.flr",
shouldClip: false,
alignment: Alignment.bottomCenter,
fit: BoxFit.contain,
controller: _teddyController,
)),),
Positioned(
child: SingleChildScrollView(
padding: EdgeInsets.only(
left: 20.0, right: 20.0, top: devicePadding.top + 150.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.all(Radius.circular(25.0))),
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TrackingTextInput(
label: "Email",
hint: "What's your email address?",
onCaretMoved: (Offset caret) {
_teddyController.lookAt(caret);
}),
TrackingTextInput(
label: "Password",
hint: "Try 'bears'...",
isObscured: true,
onCaretMoved: (Offset caret) {
_teddyController.coverEyes(caret != null);
_teddyController.lookAt(null);
},
onTextChanged: (String value) {
_teddyController.setPassword(value);
},
),
SigninButton(
child: Text("Sign In",
style: TextStyle(
fontFamily: "RobotoMedium",
fontSize: 16,
color: Colors.white)),
onPressed: () {
_teddyController.submitPassword();
})
],
)),
)),
])),
),
],
)),
);
}
}

Related

Flutter How to stack image and total member text

I was able to show the pictures as in the video by taking advantage of Johannes Milke's video that I left the link of. But that's not all I want. I need a structure that looks like these images but shows the total number of users. I leave the image of exactly what I want and my related codes.
What I want to achieve; Creating a structure where I can write the total number of users
There are a few packages on pub dev but not what I wanted. Thank you
Video:Source Video
image i want to make:
My stacked widget:
import 'package:flutter/material.dart';
class StackedWidgets extends StatelessWidget {
final List<Widget> items;
final TextDirection direction;
final double size;
final double xShift;
const StackedWidgets({
Key? key,
required this.items,
this.direction = TextDirection.ltr,
this.size = 100,
this.xShift = 20,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final allItems = items
.asMap()
.map((index, item) {
final left = size - xShift;
final value = Container(
width: size,
height: size,
child: item,
margin: EdgeInsets.only(left: left * index),
);
return MapEntry(index, value);
})
.values
.toList();
return Stack(
children: direction == TextDirection.ltr
? allItems.reversed.toList()
: allItems,
);
}
}
Usage my stacked widget:
Widget buildStackedImages({
TextDirection direction = TextDirection.ltr,
}) {
final double size = 100;
final double xShift = 20;
final urlImages = [
'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=633&q=80',
'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
'https://images.unsplash.com/photo-1616766098956-c81f12114571?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
];
final items = urlImages.map((urlImage) => buildImage(urlImage)).toList();
return StackedWidgets(
direction: direction,
items: items,
size: size,
xShift: xShift,
);
}
Widget buildImage(String urlImage) {
final double borderSize = 5;
return ClipOval(
child: Container(
padding: EdgeInsets.all(borderSize),
color: Colors.white,
child: ClipOval(
child: Image.network(
urlImage,
fit: BoxFit.cover,
),
),
),
);
}
Add a label also to this class
import 'package:flutter/material.dart';
class StackedWidgets extends StatelessWidget {
final List<Widget> items;
final TextDirection direction;
final double size;
final double xShift;
final String lable;
const StackedWidgets({
Key? key,
required this.items,
this.direction = TextDirection.ltr,
this.size = 100,
this.xShift = 20,
this.label = '',
}) : super(key: key);
#override
Widget build(BuildContext context) {
final allItems = items
.asMap()
.map((index, item) {
final left = size - xShift;
final value = Container(
width: size,
height: size,
child: item,
margin: EdgeInsets.only(left: left * index),
);
return MapEntry(index, value);
})
.values
.toList();
return Row(
children: [
Stack(
children: direction == TextDirection.ltr
? allItems.reversed.toList()
: allItems,
),
Text(label),
]
);
}
}
In items pass the number widget and label too
return StackedWidgets(
direction: direction,
items: [...items, Container(
width: 25,//you can also add padding if required
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.orange,
),
child: Text('+22'))],
size: size,
xShift: xShift,
label: "users are here",
);
I will prefer this way,
Run on dartPad
class InTEST extends StatefulWidget {
const InTEST({Key? key}) : super(key: key);
#override
State<InTEST> createState() => _InTESTState();
}
class _InTESTState extends State<InTEST> {
int maxRenderAvatar = 5;
int numberOfActiveUser = 33;
double size = 100;
double borderSize = 5;
Widget buildStackedImages({
TextDirection direction = TextDirection.ltr,
}) {
List<String> urlImages = List.filled(
numberOfActiveUser, //based on your list
'https://images.unsplash.com/photo-1554151228-14d9def656e4?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=633&q=80');
List<Widget> items = [];
final renderItemCount = numberOfActiveUser > maxRenderAvatar
? maxRenderAvatar
: urlImages.length;
for (int i = 0; i < renderItemCount; i++) {
items.add(
Positioned(
left: (i * size * .8),
child: buildImage(
urlImages[i],
),
),
);
}
// add counter if urlImages.length > maxRenderAvatar
if (numberOfActiveUser > maxRenderAvatar) {
items.add(
Positioned(
left: maxRenderAvatar * size * .8,
child: Container(
width: size,
height: size,
padding: EdgeInsets.all(borderSize),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 4),
color: Colors.amber,
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Text(
"+ ${urlImages.length - maxRenderAvatar}",
style: TextStyle(
fontSize: 23,
),
),
),
),
);
}
return SizedBox(
height: size + (borderSize * 2), //10 for borderSize
width: MediaQuery.of(context).size.width,
child: Stack(
children: items,
),
);
}
Widget buildImage(String urlImage) {
return ClipOval(
child: Container(
padding: EdgeInsets.all(borderSize),
color: Colors.white,
child: ClipOval(
child: Image.network(
urlImage,
width: size,
height: size,
fit: BoxFit.cover,
),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Slider(
value: numberOfActiveUser.toDouble(),
min: 0,
max: 55,
onChanged: (v) {
numberOfActiveUser = v.toInt();
setState(() {});
}),
Slider(
value: maxRenderAvatar.toDouble(),
min: 0,
max: 42,
onChanged: (v) {
maxRenderAvatar = v.toInt();
setState(() {});
}),
buildStackedImages(),
],
),
);
}
}

what is equivalent of linearlayout with gridlayoutmanager (android) in flutter?

I try to create horizontal listview with 2 row and many column (may horizontal scrollview) like above image. if in android I can use listview with gridlayoutmanager. how to I do in flutter?
and I want to my menu can reorderable, so I use ReorderableList, this my fullcode
import 'package:flutter/material.dart' hide ReorderableList;
import 'package:flutter_app2/home.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter_reorderable_list/flutter_reorderable_list.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class ItemData {
ItemData(this.title, this.key);
final String title;
// Each item in reorderable list needs stable and unique key
final Key key;
}
enum DraggingMode {
iOS,
Android,
}
class Item extends StatelessWidget {
Item({
required this.data,
required this.isFirst,
required this.isLast,
required this.draggingMode,
});
final ItemData data;
final bool isFirst;
final bool isLast;
final DraggingMode draggingMode;
Widget _buildChild(BuildContext context, ReorderableItemState state) {
BoxDecoration decoration;
if (state == ReorderableItemState.dragProxy ||
state == ReorderableItemState.dragProxyFinished) {
// slightly transparent background white dragging (just like on iOS)
decoration = BoxDecoration(color: Color(0xD0E81818));
} else {
bool placeholder = state == ReorderableItemState.placeholder;
decoration = BoxDecoration(
border: Border(
top: isFirst && !placeholder
? Divider.createBorderSide(context) //
: BorderSide.none,
bottom: isLast && placeholder
? BorderSide.none //
: Divider.createBorderSide(context)),
color: placeholder ? null : Colors.blue);
}
// For iOS dragging mode, there will be drag handle on the right that triggers
// reordering; For android mode it will be just an empty container
Widget dragHandle = draggingMode == DraggingMode.iOS
? ReorderableListener(
child: Container(
padding: EdgeInsets.only(right: 18.0, left: 18.0),
color: Color(0x08000000),
child: Center(
child: Icon(Icons.reorder, color: Color(0xFF6AA848)),
),
),
)
: Container();
Widget content = Container(
decoration: decoration,
child: SafeArea(
top: false,
bottom: false,
child: Opacity(
// hide content for placeholder
opacity: state == ReorderableItemState.placeholder ? 0.0 : 1.0,
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Padding(
padding:
EdgeInsets.symmetric(vertical: 14.0, horizontal: 14.0),
child: Text(data.title,
style: Theme.of(context).textTheme.subtitle1),
)),
// Triggers the reordering
dragHandle,
],
),
),
)),
);
// For android dragging mode, wrap the entire content in DelayedReorderableListener
if (draggingMode == DraggingMode.Android) {
content = DelayedReorderableListener(
child: content,
);
}
return content;
}
#override
Widget build(BuildContext context) {
return ReorderableItem(
key: data.key, //
childBuilder: _buildChild);
}
}
class _MyHomePageState extends State<MyHomePage> {
late List<ItemData> _items;
_MyHomePageState() {
_items = [];
for (int i = 0; i < 500; ++i) {
String label = "List item $i";
if (i == 5) {
label += ". This item has a long label and will be wrapped.";
}
_items.add(ItemData(label, ValueKey(i)));
}
}
// Returns index of item with given key
int _indexOfKey(Key key) {
return _items.indexWhere((ItemData d) => d.key == key);
}
bool _reorderCallback(Key item, Key newPosition) {
int draggingIndex = _indexOfKey(item);
int newPositionIndex = _indexOfKey(newPosition);
final draggedItem = _items[draggingIndex];
setState(() {
debugPrint("Reordering $item -> $newPosition");
_items.removeAt(draggingIndex);
_items.insert(newPositionIndex, draggedItem);
});
return true;
}
void _reorderDone(Key item) {
final draggedItem = _items[_indexOfKey(item)];
debugPrint("Reordering finished for ${draggedItem.title}}");
}
DraggingMode _draggingMode = DraggingMode.iOS;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ReorderableList(
onReorder: this._reorderCallback,
onReorderDone: this._reorderDone,
child: CustomScrollView(
// cacheExtent: 3000,
slivers: <Widget>[
SliverPadding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Item(
data: _items[index],
// first and last attributes affect border drawn during dragging
isFirst: index == 0,
isLast: index == _items.length - 1,
draggingMode: _draggingMode,
);
},
childCount: _items.length,
),
)),
],
),
),
);
}
}
and this my current view
what about using reorderables, ReorderableWrap:
class _NestedWrapExampleState extends State<NestedWrapExample> {
// List<Widget> _tiles;
Color _color;
Color _colorBrighter;
#override
void initState() {
super.initState();
_color = widget.color ?? Colors.primaries[widget.depth % Colors.primaries.length];
_colorBrighter = Color.lerp(_color, Colors.white, 0.6);
}
#override
Widget build(BuildContext context) {
void _onReorder(int oldIndex, int newIndex) {
setState(() {
widget._tiles.insert(newIndex, widget._tiles.removeAt(oldIndex));
});
}
var wrap = ReorderableWrap(
spacing: 8.0,
runSpacing: 4.0,
padding: const EdgeInsets.all(8),
children: widget._tiles,
onReorder: _onReorder
);
var buttonBar = Container(
color: _colorBrighter,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
iconSize: 42,
icon: Icon(Icons.add_circle),
color: Colors.deepOrange,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
widget._tiles.add(
Card(
child: Container(
child: Text('${widget.valuePrefix}${widget._tiles.length}', textScaleFactor: 3 / math.sqrt(widget.depth + 1)),
padding: EdgeInsets.all((24.0 / math.sqrt(widget.depth + 1)).roundToDouble()),
),
color: _colorBrighter,
elevation: 3,
)
);
});
},
),
IconButton(
iconSize: 42,
icon: Icon(Icons.remove_circle),
color: Colors.teal,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
widget._tiles.removeAt(0);
});
},
),
IconButton(
iconSize: 42,
icon: Icon(Icons.add_to_photos),
color: Colors.pink,
padding: const EdgeInsets.all(0.0),
onPressed: () {
setState(() {
widget._tiles.add(NestedWrapExample(depth: widget.depth + 1, valuePrefix: '${widget.valuePrefix}${widget._tiles.length}.',));
});
},
),
Text('Level ${widget.depth} / ${widget.valuePrefix}'),
],
)
);
var column = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buttonBar,
wrap,
]
);
return SingleChildScrollView(
child: Container(child: column, color: _color,),
);
}
}

Flutter: How to set boundaries for a Draggable widget?

I'm trying to create a drag and drop game. I would like to make sure that the Draggable widgets don't get out of the screen when they are dragged around.
I couldn't find an answer to this specific question. Someone asked something similar about constraining draggable area Constraining Draggable area but the answer doesn't actually make use of Draggable.
To start with I tried to implement a limit on the left-hand side.
I tried to use a Listener with onPointerMove. I've associated this event with a limitBoundaries method to detect when the Draggable exits from the left side of the screen. This part is working as it does print in the console the Offset value when the Draggable is going out (position.dx < 0). I also associated a setState to this method to set the position of the draggable to Offset(0.0, position.dy) but this doesn't work.
Could anybody help me with this?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
}
}
class GamePlay extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
}
}
class DragObject extends StatefulWidget {
final String id;
final Offset initPos;
final Color itmColor;
DragObject({Key key, this.id, this.initPos, this.itmColor}) : super(key: key);
#override
_DragObjectState createState() => _DragObjectState();
}
class _DragObjectState extends State<DragObject> {
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
}
void _getRenderOffsets() {
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
}
void _afterLayout(_) {
_getRenderOffsets();
}
void limitBoundaries(PointerEvent details) {
if (details.position.dx < 0) {
print(details.position);
setState(() {
position = Offset(0.0, position.dy);
});
}
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
onPointerMove: limitBoundaries,
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag) {
setState(() {
position = drag.offset - posOffset;
});
},
),
),
);
}
}
Try this. I tweaked this from: Constraining Draggable area .
ValueNotifier<List<double>> posValueListener = ValueNotifier([0.0, 0.0]);
ValueChanged<List<double>> posValueChanged;
double _horizontalPos = 0.0;
double _verticalPos = 0.0;
#override
void initState() {
super.initState();
posValueListener.addListener(() {
if (posValueChanged != null) {
posValueChanged(posValueListener.value);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
_buildDraggable(),
]));
}
_buildDraggable() {
return SafeArea(
child: Container(
margin: EdgeInsets.only(bottom: 100),
color: Colors.green,
child: Builder(
builder: (context) {
final handle = GestureDetector(
onPanUpdate: (details) {
_verticalPos =
(_verticalPos + details.delta.dy / (context.size.height))
.clamp(.0, 1.0);
_horizontalPos =
(_horizontalPos + details.delta.dx / (context.size.width))
.clamp(.0, 1.0);
posValueListener.value = [_horizontalPos, _verticalPos];
},
child: Container(
child: Container(
margin: EdgeInsets.all(12),
width: 110.0,
height: 170.0,
child: Container(
color: Colors.black87,
),
decoration: BoxDecoration(color: Colors.black54),
),
));
return ValueListenableBuilder<List<double>>(
valueListenable: posValueListener,
builder:
(BuildContext context, List<double> value, Widget child) {
return Align(
alignment: Alignment(value[0] * 2 - 1, value[1] * 2 - 1),
child: handle,
);
},
);
},
),
),
);
}
I've found a workaround for this issue. It's not exactly the output I was looking for but I thought this could be useful to somebody else.
Instead of trying to control the drag object during dragging, I just let it go outside of my screen and I placed it back to its original position in case it goes outside of the screen.
Just a quick note if someone tries my code, I forgot to mention that I'm trying to develop a game for the web. The output on a mobile device might be a little bit odd!
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
home: GamePlay(),
);
}
}
class GamePlay extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Row(
children: [
Container(
width: 360,
height: 400,
decoration: BoxDecoration(
color: Colors.lightGreen,
border: Border.all(
color: Colors.green,
width: 2.0,
),
),
),
Container(
width: 190,
height: 400,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.purple,
width: 2.0,
),
),
),
],
),
DragObject(
key: GlobalKey(),
initPos: Offset(365, 0.0),
id: 'Item 1',
itmColor: Colors.orange),
DragObject(
key: GlobalKey(),
initPos: Offset(450, 0.0),
id: 'Item 2',
itmColor: Colors.pink,
),
],
),
);
}
}
class DragObject extends StatefulWidget {
final String id;
final Offset initPos;
final Color itmColor;
DragObject({Key key, this.id, this.initPos, this.itmColor}) : super(key: key);
#override
_DragObjectState createState() => _DragObjectState();
}
class _DragObjectState extends State<DragObject> {
GlobalKey _key;
Offset position;
Offset posOffset = Offset(0.0, 0.0);
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_key = widget.key;
position = widget.initPos;
super.initState();
}
void _getRenderOffsets() {
final RenderBox renderBoxWidget = _key.currentContext.findRenderObject();
final offset = renderBoxWidget.localToGlobal(Offset.zero);
posOffset = offset - position;
}
void _afterLayout(_) {
_getRenderOffsets();
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Listener(
child: Draggable(
child: Container(
width: 80,
height: 80,
color: widget.itmColor,
),
feedback: Container(
width: 82,
height: 82,
color: widget.itmColor,
),
childWhenDragging: Container(),
onDragEnd: (drag) {
setState(() {
if (drag.offset.dx > 0) {
position = drag.offset - posOffset;
} else {
position = widget.initPos;
}
});
},
),
),
);
}
}
I'm still interested if someone can find a proper solution to the initial issue :-)
you could use the property onDragEnd: of the widget Draggable and before setting the new position compare it with the height or width of your device using MediaQuery and update only if you didn't pass the limits of your screen, else set the new position to the initial one.
Example bellow :
Positioned(
left: position.dx,
top: position.dy,
child: Draggable(
maxSimultaneousDrags: 1,
childWhenDragging:
Opacity(opacity: .2, child: rangeEvent(context)),
feedback: rangeEvent(context),
axis: Axis.vertical,
affinity: Axis.vertical,
onDragEnd: (details) => updatePosition(details.offset),
child: Transform.scale(
scale: scale,
child: rangeEvent(context),
),
),
)
In the method updatePosition, you verify the new position before updating:
void updatePosition(Offset newPosition) => setState(() {
if (newPosition.dy > 10 &&
newPosition.dy < MediaQuery.of(context).size.height * 0.9) {
position = newPosition;
} else {
position = const Offset(0, 0);// initial possition
}
});

Palette Generator library for online image source doesn't work in Flutter

I have used the following code for get the dominant color of an online image but the paletter color remains null. How can I solve this problem?
N:B: If I use local image source AssetImage("source_path"),it works.
class HomePageUI extends StatefulWidget {
final HomeData _homeData;
HomePageUI(this._homeData);
#override
_HomePageUIState createState() => _HomePageUIState();
}
class _HomePageUIState extends State<HomePageUI> {
PaletteColor paletteColor;
#override
void initState(){
super.initState();
_updatePalettes();
}
_updatePalettes() async{
final PaletteGenerator paletteGenerator = await PaletteGenerator.fromImageProvider(
NetworkImage(NetworkConfig.getImageUrl(widget._homeData.trending[1].image)),
size: Size(200,100)
);
paletteColor = paletteGenerator.darkMutedColor;
//Here paletteColor is shown null value
}
}
You can copy paste run full code below
You do not need to modify _updatePalettes()
You can directly use NetworkImage and pass correct imageSize
code snippet
home: const ImageColors(
title: 'Image Colors',
image: NetworkImage('https://picsum.photos/250?image=9'), //AssetImage('assets/landscape.png'),
imageSize: Size(250.0, 250.0),
),
working demo
full code
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:palette_generator/palette_generator.dart';
void main() => runApp(MyApp());
const Color _kBackgroundColor = Color(0xffa0a0a0);
const Color _kSelectionRectangleBackground = Color(0x15000000);
const Color _kSelectionRectangleBorder = Color(0x80000000);
const Color _kPlaceholderColor = Color(0x80404040);
/// The main Application class.
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Colors',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: const ImageColors(
title: 'Image Colors',
image: NetworkImage('https://picsum.photos/250?image=9'), //AssetImage('assets/landscape.png'),
imageSize: Size(250.0, 250.0),
),
);
}
}
/// The home page for this example app.
#immutable
class ImageColors extends StatefulWidget {
/// Creates the home page.
const ImageColors({
Key key,
this.title,
this.image,
this.imageSize,
}) : super(key: key);
/// The title that is shown at the top of the page.
final String title;
/// This is the image provider that is used to load the colors from.
final ImageProvider image;
/// The dimensions of the image.
final Size imageSize;
#override
_ImageColorsState createState() {
return _ImageColorsState();
}
}
class _ImageColorsState extends State<ImageColors> {
Rect region;
Rect dragRegion;
Offset startDrag;
Offset currentDrag;
PaletteGenerator paletteGenerator;
final GlobalKey imageKey = GlobalKey();
#override
void initState() {
super.initState();
region = Offset.zero & widget.imageSize;
_updatePaletteGenerator(region);
}
Future<void> _updatePaletteGenerator(Rect newRegion) async {
paletteGenerator = await PaletteGenerator.fromImageProvider(
widget.image,
size: widget.imageSize,
region: newRegion,
maximumColorCount: 20,
);
setState(() {});
}
// Called when the user starts to drag
void _onPanDown(DragDownDetails details) {
final RenderBox box = imageKey.currentContext.findRenderObject();
final Offset localPosition = box.globalToLocal(details.globalPosition);
setState(() {
startDrag = localPosition;
currentDrag = startDrag;
dragRegion = Rect.fromPoints(startDrag, currentDrag);
});
}
// Called as the user drags: just updates the region, not the colors.
void _onPanUpdate(DragUpdateDetails details) {
setState(() {
currentDrag += details.delta;
dragRegion = Rect.fromPoints(startDrag, currentDrag);
});
}
// Called if the drag is canceled (e.g. by rotating the device or switching
// apps)
void _onPanCancel() {
setState(() {
dragRegion = null;
startDrag = null;
});
}
// Called when the drag ends. Sets the region, and updates the colors.
Future<void> _onPanEnd(DragEndDetails details) async {
Rect newRegion =
(Offset.zero & imageKey.currentContext.size).intersect(dragRegion);
if (newRegion.size.width < 4 && newRegion.size.width < 4) {
newRegion = Offset.zero & imageKey.currentContext.size;
}
await _updatePaletteGenerator(newRegion);
setState(() {
region = newRegion;
dragRegion = null;
startDrag = null;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: _kBackgroundColor,
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
// GestureDetector is used to handle the selection rectangle.
child: GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
onPanCancel: _onPanCancel,
onPanEnd: _onPanEnd,
child: Stack(children: <Widget>[
Image(
key: imageKey,
image: widget.image,
width: widget.imageSize.width,
height: widget.imageSize.height,
),
// This is the selection rectangle.
Positioned.fromRect(
rect: dragRegion ?? region ?? Rect.zero,
child: Container(
decoration: BoxDecoration(
color: _kSelectionRectangleBackground,
border: Border.all(
width: 1.0,
color: _kSelectionRectangleBorder,
style: BorderStyle.solid,
)),
)),
]),
),
),
// Use a FutureBuilder so that the palettes will be displayed when
// the palette generator is done generating its data.
PaletteSwatches(generator: paletteGenerator),
],
),
);
}
}
/// A widget that draws the swatches for the [PaletteGenerator] it is given,
/// and shows the selected target colors.
class PaletteSwatches extends StatelessWidget {
/// Create a Palette swatch.
///
/// The [generator] is optional. If it is null, then the display will
/// just be an empty container.
const PaletteSwatches({Key key, this.generator}) : super(key: key);
/// The [PaletteGenerator] that contains all of the swatches that we're going
/// to display.
final PaletteGenerator generator;
#override
Widget build(BuildContext context) {
final List<Widget> swatches = <Widget>[];
if (generator == null || generator.colors.isEmpty) {
return Container();
}
for (Color color in generator.colors) {
swatches.add(PaletteSwatch(color: color));
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Wrap(
children: swatches,
),
Container(height: 30.0),
PaletteSwatch(label: 'Dominant', color: generator.dominantColor?.color),
PaletteSwatch(
label: 'Light Vibrant', color: generator.lightVibrantColor?.color),
PaletteSwatch(label: 'Vibrant', color: generator.vibrantColor?.color),
PaletteSwatch(
label: 'Dark Vibrant', color: generator.darkVibrantColor?.color),
PaletteSwatch(
label: 'Light Muted', color: generator.lightMutedColor?.color),
PaletteSwatch(label: 'Muted', color: generator.mutedColor?.color),
PaletteSwatch(
label: 'Dark Muted', color: generator.darkMutedColor?.color),
],
);
}
}
/// A small square of color with an optional label.
#immutable
class PaletteSwatch extends StatelessWidget {
/// Creates a PaletteSwatch.
///
/// If the [color] argument is omitted, then the swatch will show a
/// placeholder instead, to indicate that there is no color.
const PaletteSwatch({
Key key,
this.color,
this.label,
}) : super(key: key);
/// The color of the swatch. May be null.
final Color color;
/// The optional label to display next to the swatch.
final String label;
#override
Widget build(BuildContext context) {
// Compute the "distance" of the color swatch and the background color
// so that we can put a border around those color swatches that are too
// close to the background's saturation and lightness. We ignore hue for
// the comparison.
final HSLColor hslColor = HSLColor.fromColor(color ?? Colors.transparent);
final HSLColor backgroundAsHsl = HSLColor.fromColor(_kBackgroundColor);
final double colorDistance = math.sqrt(
math.pow(hslColor.saturation - backgroundAsHsl.saturation, 2.0) +
math.pow(hslColor.lightness - backgroundAsHsl.lightness, 2.0));
Widget swatch = Padding(
padding: const EdgeInsets.all(2.0),
child: color == null
? const Placeholder(
fallbackWidth: 34.0,
fallbackHeight: 20.0,
color: Color(0xff404040),
strokeWidth: 2.0,
)
: Container(
decoration: BoxDecoration(
color: color,
border: Border.all(
width: 1.0,
color: _kPlaceholderColor,
style: colorDistance < 0.2
? BorderStyle.solid
: BorderStyle.none,
)),
width: 34.0,
height: 20.0,
),
);
if (label != null) {
swatch = ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 130.0, minWidth: 130.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
swatch,
Container(width: 5.0),
Text(label),
],
),
);
}
return swatch;
}
}
You can check here how you can do it by yourself:
https://jelenaaa.medium.com/how-to-extract-a-color-palette-from-an-image-in-flutter-dart-d3d49699a5eb
You decode you image, extract a mesh of pixels, sort and arrange their colors and generate a palette of X colors.

Multiple GestureDetector onPan event in Stack not working

I have to implement a custom analog clock where the clock hands indicate the breakfast,lunch and dinner time and user is able to reset the time by rotating the hands. I was able to draw the clock and hands with the help of canvas and CustomPaint and i haved stacked up in Stack Widget.I have added GestureDetector for each hand and using onPan events am able to rotate the hands also.But the issue is that when multiple hands are put in Stack only the last added clock hand is moving with the gesture others are not detecting the gesture.How to pass down the event to the stack so that all the gesture detectors detects it?
For drawing clock hands
class ClockHandComponent extends StatefulWidget {
final String title;
final int time;
final Color color;
const ClockHandComponent({Key key, this.title, this.time, this.color})
: super(key: key);
#override
ClockHandComponentState createState() {
return new ClockHandComponentState();
}
}
class ClockHandComponentState extends State<ClockHandComponent> {
int time;
double angle;
ClockHandPainter _clockHandPainter;
#override
void initState() {
time = widget.time;
angle = 2 * pi / 24 * time;
initialize();
super.initState();
}
#override
void didUpdateWidget(ClockHandComponent oldWidget) {
initialize();
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanDown: _onPanDown,
onPanUpdate: _onPanUpdate,
onPanEnd: _onPanEnd,
child: new Container(
width: double.infinity,
height: double.infinity,
child: new CustomPaint(
painter: _clockHandPainter,
)),
);
}
_onPanUpdate(DragUpdateDetails details) {
print(details);
RenderBox renderBox = context.findRenderObject();
var position = renderBox.globalToLocal(details.globalPosition);
angle = -coordinatesToRadians(_clockHandPainter.center, position);
print(angle);
initialize();
setState(() {});
}
_onPanEnd(_) {}
_onPanDown(DragDownDetails details) {
print(details);
print(angle);
}
initialize() {
_clockHandPainter =
new ClockHandPainter(widget.title, time, widget.color, angle);
}
double coordinatesToRadians(Offset center, Offset coords) {
var a = coords.dx - center.dx;
var b = center.dy - coords.dy;
return atan2(b, a);
}
}
For drawing clock face
class ClockFaceComponent extends StatefulWidget {
#override
ClockFaceComponentState createState() {
return new ClockFaceComponentState();
}
}
class ClockFaceComponentState extends State<ClockFaceComponent> {
#override
Widget build(BuildContext context) {
return new Padding(
padding: const EdgeInsets.all(10.0),
child: new AspectRatio(
aspectRatio: 1.0,
child: new Container(
width: double.infinity,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: ColorResource.clockBackgroundColor,
),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
//dial and numbers
new Container(
width: double.infinity,
height: double.infinity,
child: new CustomPaint(
painter: new ClockDialPainter(),
),
),
new Center(
child: new Container(
width: 40.0,
height: 40.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
)),
//centerpoint
new Center(
child: new Container(
width: 15.0,
height: 15.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: ColorResource.clockCenterColor,
),
),
),
getClockHands()
],
),
),
),
);
}
getClockHands() {
return new AspectRatio(
aspectRatio: 1.0,
child: new Stack(
fit: StackFit.expand,
children: getHands(),
));
}
getHands() {
List<Widget> widgets = new List();
List<Hands> hands = Hands.getHands();
for (Hands hand in hands) {
widgets.add(ClockHandComponent(
title: hand.title,
time: hand.time,
color: hand.color,
));
}
return widgets;
}
}