So we have a Dismissible for confirming/denying a item.
However we have some users that are trying to click/tap on the item.
Our UX team suggested that we then "bounce" the item to show that they have to swipe (and reveal the action fields).
But I don't see any option to do so.
Does anybody have a suggestion what might work for this?
The code I have for now is shown below:
Dismissible(
key: const ValueKey(0),
direction: DismissDirection.horizontal,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 3, vertical: 3),
child: card(),
),
confirmDismiss: (direction) async {
var newStatus = direction == DismissDirection.startToEnd
? OkNokNvt.OK
: OkNokNvt.NOK;
_changeStatus(newStatus);
return false;
},
background: ok(),
secondaryBackground: nok(),
),
The Dismissable doesn't seeem to have this functionality.
Instead, you could use the flutter_slidable package.
Here, you can programmatically open the underlying actions by calling Slideable.of(context)?.open(). No fancy bounce-animation though.
Here's the code:
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bouncing Widget Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Slidable(
key: const Key('key'),
actionPane: const SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
child: Builder(
builder: (context) => GestureDetector(
onTap: () {
Slidable.of(context)
?.open(actionType: SlideActionType.primary);
},
child: Container(
color: Colors.grey,
height: 50,
child: const Center(child: Text('Tap me')),
),
),
),
actions: [
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => print('remove me from list'),
),
],
dismissal: SlidableDismissal(
onDismissed: (_) => print('remove me from list'),
dragDismissible: true,
child: const SlidableDrawerDismissal(),
),
),
],
),
),
);
}
}
Here is my minimal example which does what you are looking for.
Basically, the GestureDetector onTap callback triggers the animation which has a bouncing-like effect by using a sin function on the _animation.value. The behaviour can be tweeked by changing the parameters cyclesPerAnimation and bounceOffset.
Simply put your Dismissible in the place of the Container and you should be good to go.
environment:
sdk: ">=2.12.0 <3.0.0"
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late final AnimationController _animation = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
Offset _bounceOffset(double animationValue) {
const cyclesPerAnimation = 2;
const bounceOffset = 10;
return Offset(
0,
sin(animationValue * pi * 2 * cyclesPerAnimation) * bounceOffset,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bouncing Widget Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedBuilder(
animation: _animation,
builder: (context, widget) => Transform.translate(
offset: _bounceOffset(_animation.value),
child: GestureDetector(
onTap: () {
_animation.reset();
_animation.forward();
},
child: Container(
color: Colors.grey,
height: 50,
width: 200,
child: const Center(child: Text('Tap to bounce')),
),
),
),
),
],
),
),
);
}
}
Related
I would like to display last 3 images in the camera roll/gallery/photos from my app. How do I achieve this in Flutter?
Any ideas?
Suppose I want to see the latest images in the DCIM folder. How do we do this?
I hope what you're looking for will be solved by using this package photo_gallery
Never used this package before, but it seems to fit your needs.
Try to use media_picker_widget as its supports presenting specific amount of images from different albums using custom widgets.
Check out the official example:
import 'package:flutter/material.dart';
import 'package:media_picker_widget/media_picker_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Media Picker',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Media> mediaList = [];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Image Picker'),
),
body: previewList(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => openImagePicker(context),
),
);
}
Widget previewList() {
return SizedBox(
height: 96,
child: ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: List.generate(
mediaList.length,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
height: 80,
width: 80,
child: Image.memory(
mediaList[index].thumbnail,
fit: BoxFit.cover,
),
),
)),
),
);
}
void openImagePicker(BuildContext context) {
// openCamera(onCapture: (image){
// setState(()=> mediaList = [image]);
// });
showModalBottomSheet(
context: context,
builder: (context) {
return MediaPicker(
mediaList: mediaList,
onPick: (selectedList) {
setState(() => mediaList = selectedList);
Navigator.pop(context);
},
onCancel: () => Navigator.pop(context),
mediaCount: MediaCount.multiple,
mediaType: MediaType.image,
decoration: PickerDecoration(
actionBarPosition: ActionBarPosition.top,
blurStrength: 2,
completeText: 'Next',
),
);
});
}
}
I want to customize the CircularCountdownTimer so that you can run it without a limited duration. After one hour the ring should be completely filled and the animation should start again from 0 after 1 hour. Does anyone know how to do this?
Here is my code
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CountDownController controller = CountDownController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularCountDownTimer(
width: 300,
height: 300,
duration: 60,
fillColor: Colors.blue,
ringColor: Colors.blue.withOpacity(0.25),
autoStart: false,
controller: controller,
),
const SizedBox(
height: 100,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextButton(
onPressed: () {
controller.start();
},
child: const Text("Start"),
),
TextButton(
onPressed: () {
controller.pause();
},
child: const Text("Pause"),
),
],
),
],
),
),
);
}
}
You can use onComplete to restart the animation.
CircularCountDownTimer(
width: 300,
height: 300,
duration: 60, // it is on seconds,
fillColor: Colors.blue,
ringColor: Colors.blue.withOpacity(0.25),
autoStart: false,
controller: controller,
onComplete: () {
controller.restart();
},
),
In the application, the home page is ResultScreen, which displays the entered data. If they are not there, then when you click on the button, we go to the screen with the input. When I enter text into the input and click on the Display Result button, the data should be substituted into the text field on the first screen. I implemented such functionality, but I don’t understand what argument I should substitute in main.dart. Tell me please
Text Screen:
import 'package:flutter/material.dart';
import 'package:flutter_application_1/screens/result_screen.dart';
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: InputDecoration(labelText: 'Message'),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ResultScreen(textController.text)));
},
child: Text('Display result'))
],
)),
);
}
}
Result Screen:
import 'package:flutter/material.dart';
import 'package:flutter_application_1/screens/text_screen.dart';
class ResultScreen extends StatefulWidget {
final String valueText;
ResultScreen(this.valueText);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
// navigation to text_screen
void _buttonNav() {
Navigator.push(
context, MaterialPageRoute(builder: (context) => const TextScreen()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _buttonNav, child: const Text('Enter data')),
const SizedBox(
height: 50,
),
Text(valueText),
const SizedBox(
height: 20,
),
],
)),
);
}
}
Main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_application_1/screens/result_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ResultScreen(),
);
}
}
Use the following code.
What is does is, when we enter the first screen i.e. ResultScreen, we pass an empty value for the first time.
Use this in main.dart
home: ResultScreen(''),
And as you are using statefull widget for ResultScreen, you need to use widget.valueText to access it like:
Text(widget.valueText),
I have a ListView with ListTiles which set the tileColor property. All of the text and icons in the ListTile is clipped while the tileColor is visible beyond the ListView. How can I clip the entire ListTile so that nothing is visible beyond the ListView? I have tried wrapping ListView in various containers and the ListTile in a Container with no luck.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: const MyHomePage(title: 'Reminders'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _reminders = <String>[];
void _addReminder(String text) {
setState(() {
_reminders.add(text);
});
}
Widget _buildRow(String text, int i) {
return ListTile(
title: Text('[enter image description here][1]Reminder'),
trailing: IconButton(
onPressed: () {
setState(() {
_reminders.removeAt(i);
});
},
icon: Icon(Icons.delete)),
subtitle: Text(text),
tileColor: Colors.lightBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
);
}
void _showMenu() {}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [IconButton(onPressed: _showMenu, icon: const Icon(Icons.list))],
),
body: Column(
children: <Widget>[
Container(
height: 400,
child: ListView.builder(
padding: EdgeInsets.all(8),
itemBuilder: (context, i) => _buildRow(_reminders[i], i),
itemCount: _reminders.length,
),
),
Container(
height: 100,
width: double.infinity,
child: Text('hello'),
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_addReminder('hello there');
},
child: const Icon(Icons.add),
),
);
}
}
To solve your problem, wrap the Tile in a Material. Here is a tested solution. The explanation is below the code.
The short version:
Widget _buildRow(String text, int i) {
return Material(
child: ListTile(
title: Text('[enter image description here][$i]Reminder'),
trailing: IconButton(
onPressed: () {
setState(() {
_reminders.removeAt(i);
});
},
icon: const Icon(Icons.delete)),
subtitle: Text(text),
tileColor: Colors.lightBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
));
}
The complete, working example:
```
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: const MyHomePage(title: 'Reminders'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List _reminders = <String>[];
void _addReminder(String text) {
setState(() {
_reminders.add(text);
});
}
Widget _buildRow(String text, int i) {
return Material(child: ListTile(
title: Text('[enter image description here][$i]Reminder'),
trailing: IconButton(
onPressed: () {
setState(() {
_reminders.removeAt(i);
});
},
icon: const Icon(Icons.delete)),
subtitle: Text(text),
tileColor: Colors.lightBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
));
}
void _showMenu() {}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [IconButton(onPressed: _showMenu, icon: const Icon(Icons.list))],
),
body: Column(
children: <Widget>[
Container(
height: 400,
child: ClipRect(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemBuilder: (context, i) => _buildRow(_reminders[i], i),
itemCount: _reminders.length,
),
)
),
Container(
color: Colors.white,
width: double.infinity,
child: Text('hello'),
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_addReminder('hello there');
},
child: const Icon(Icons.add),
),
);
}
}
```
This might be considered a bug, but it is expected behavior. The tile colors are painted by the first Material widget ancestor of the ListTile (I believe this allows it to optimize out expensive clipping). See the ListTile documentation for more information.
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: const MyHomePage(title: 'Reminders'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// ignore: prefer_final_fields
List _reminders = <String>[];
void _addReminder(String text) {
setState(() {
_reminders.add(text);
});
}
Widget _buildRow(String text, int i) {
return ListTile(
title: const Text('Reminder'),
trailing: IconButton(
onPressed: () {
setState(() {
_reminders.removeAt(i);
});
},
icon: const Icon(Icons.delete)),
subtitle: Text(text),
tileColor: Colors.lightBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
);
}
void _showMenu() {}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [IconButton(onPressed: _showMenu, icon: const Icon(Icons.list))],
),
body: ListView.separated(
padding: const EdgeInsets.all(8),
itemBuilder: (context, i) => _buildRow(_reminders[i], i),
itemCount: _reminders.length, separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height:10);},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_addReminder('hello there');
},
child: const Icon(Icons.add),
),
);
}
}
Here is my code with solution
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: const MyHomePage(title: 'Reminders'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _reminders = <String>[];
void _addReminder(String text) {
setState(() {
_reminders.add(text);
});
}
Widget _buildRow(String text, int i) {
return Container(
margin: EdgeInsets.symmetric(vertical: 5),
child: ListTile(
title: Text('[enter image description here][1]Reminder'),
trailing: IconButton(
onPressed: () {
setState(() {
_reminders.removeAt(i);
});
},
icon: Icon(Icons.delete)),
subtitle: Text(text),
tileColor: Colors.lightBlue,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
);
}
void _showMenu() {}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: [
IconButton(onPressed: _showMenu, icon: const Icon(Icons.list))
],
),
body: Column(
children: <Widget>[
SizedBox(
height: 200,
child: ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.all(8),
itemBuilder: (context, i) => _buildRow(_reminders[i], i),
itemCount: _reminders.length,
),
),
Expanded(
child: Container(
color: Colors.white,
width: double.infinity,
child: Text('hello'),
),
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_addReminder('hello there');
},
child: const Icon(Icons.add),
),
);
}
}
here is ss
I implement a Room Chat Screen and inside it an animated Container move from left to right and start a again after finish, could someone inform me how to implement it
You need looping animate container, then you can try some Marquee package, such as fast_marquee:
Marquee(
text: 'Some sample text that takes some space.',
style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold),
velocity: 100,
blankSpace: 10,
startPadding: 10,
reverse: true,
bounce: true,
startAfter: const Duration(seconds: 2),
pauseAfterRound: const Duration(seconds: 1),
numberOfRounds: 5,
showFadingOnlyWhenScrolling: false,
fadingEdgeStartFraction: 0.05,
fadingEdgeEndFraction: 0.05,
curve: Curves.easeInOut,
)
OR
try AnimatedPositioned:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool selected = false;
#override
Widget build(BuildContext context) {
return SizedBox(
width: 200,
height: 350,
child: Stack(
children: <Widget>[
AnimatedPositioned(
width: selected ? 200.0 : 50.0,
height: selected ? 50.0 : 200.0,
top: selected ? 50.0 : 150.0,
duration: const Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
child: GestureDetector(
onTap: () {
setState(() {
selected = !selected;
});
},
child: Container(
color: Colors.blue,
child: const Center(child: Text('Tap me')),
),
),
),
],
),
);
}
}