View Last 3 Most Recent Images Taken With Camera In Flutter - flutter

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',
),
);
});
}
}

Related

CustomPainter's paint method is not getting called before WidgetsBinding.instance.addPostFrameCallback in case of Multiple navigation

I have a Flutter StatefulWidget and in initState() method I am using WidgetsBinding.instance.addPostFrameCallback to use one instance variable (late List _tracks). like -
WidgetsBinding.instance.addPostFrameCallback((_) {
for(itr = 0; itr<_tracks.length; itr++){
// some logic
}
});
As this would get invoked after all Widgets are done. In one of the CustomPaint's painter class I am initializing that variable.
SizedBox.expand(
child: CustomPaint(
painter: TrackPainter(
trackCalculationListener: (tracks) {
_tracks = tracks;
}),
),
),
It is working fine when I have one screen, i.e the same class. But, When I am adding one screen before that and trying to navigate to this screen from the new screen it is throwing _tracks is not initialized exception.
new screen is very basic -
class MainMenu extends StatefulWidget {
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Play(),
maintainState: false));
},
child: const Text('play game'),
),
),
);
}
}
In single screen case the paint method of painter is getting called before postFrameCallback but in case of multiple it is not getting before postFrameCallback and because of that the variable is not getting initialized.
reproducible code -
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/mainMenu': (context) => const MainMenu(),
'/game': (context) => const MyHomePage(title: 'game'),
},
initialRoute: '/mainMenu',
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<Rect> _playerTracks;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
print(_playerTracks.length);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
color: Colors.white,
margin: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: 1,
child: SizedBox.expand(
child: CustomPaint(
painter: RectanglePainter(
trackCalculationListener: (playerTracks) =>
_playerTracks = playerTracks),
),
),
),
)
],
),
),
);
}
}
class MainMenu extends StatefulWidget {
static String route = '/mainMenu';
const MainMenu({super.key});
#override
State<MainMenu> createState() => _MainMenuState();
}
class _MainMenuState extends State<MainMenu> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 200.0,
color: Colors.white,
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/game');
},
child: const Text('play game'),
),
),
),
);
}
}
class RectanglePainter extends CustomPainter {
Function(List<Rect>) trackCalculationListener;
RectanglePainter({required this.trackCalculationListener});
#override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const RadialGradient gradient = RadialGradient(
center: Alignment(0.7, -0.6),
radius: 0.2,
colors: <Color>[Color(0xFFFFFF00), Color(0xFF0099FF)],
stops: <double>[0.4, 1.0],
);
canvas.drawRect(
rect,
Paint()..shader = gradient.createShader(rect),
);
List<Rect> _playerTracks = [];
_playerTracks.add(rect);
trackCalculationListener(_playerTracks);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
I am very new to flutter and would highly appreciate if someone could help me figure out what I am doing wrong here.

My screen doesn't reflect the changes in my app though the setState method works well

I'm trying to call a StatefulWidget(i.e FirstPage()) within a MaterialApp. I'm pretty much new to flutter and I don't know where I went wrong. According to my knownledge I've used StatefulWidget to tell flutter my screen on that page is going to encounter some changes in UI. But I got no idea to fix this error.
main.dart file:
import 'package:flutter/material.dart';
import 'package:flutter_project/main.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstPage());
}
}
class FirstPage extends StatefulWidget {
const FirstPage({Key? key}) : super(key: key);
#override
State<FirstPage> createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
Widget build(BuildContext context) {
String buttonName = "Click";
int currentIndex = 0;
return Scaffold(
appBar: AppBar(
title: const Text("App title "),
),
body: Center(
child: currentIndex == 0
? Container(
width: double.infinity,
height: double.infinity,
color: Colors.blueAccent,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 280,
height: 80,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
side: BorderSide.none,
borderRadius: BorderRadius.circular(18),
),
backgroundColor: const Color.fromRGBO(9, 8, 99, 90),
foregroundColor: Colors.red,
),
onPressed: () {
setState(() {
buttonName = "Clicked";
//print(buttonName0);
});
},
child: Text(buttonName),
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
//'BuildContext' - datatype and 'context' - variable name
return const SecondPage();
},
),
);
},
child: const Text("Move to new page"),
),
],
),
)
: Image.asset("images/img.png"),
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(label: "Home", icon: Icon(Icons.home)),
BottomNavigationBarItem(label: "Settings", icon: Icon(Icons.settings))
],
currentIndex: currentIndex,
onTap: (int index) {
//index value here is returned by flutter by the function 'onTap'
setState(() {
currentIndex = index;
//print(currentIndex);
});
},
),
); //Scaffold represents the skeleton of the app(displays white page)
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
);
}
}
Images:
Before pressing Click and Settings button
After pressing Click and Settings looks the same
I want the screen to change the ElevatedButton Click to Clicked when onPressed() is triggered and also, the app should be able to switch settings page when the onTap() method is triggered in the bottom navigation bar.
The code worked initially when I refrained from calling an entire page of Scaffold from Material app, but as soon as I changed the part
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstPage()); //<-- this part
}
}
I'm getting this error.
Put your variables outside the build method.Else it will reset to default on every build.
It will be like
class _FirstPageState extends State<FirstPage> {
//here
String buttonName = "Click";
int currentIndex = 0;
#override
Widget build(BuildContext context) {
// Not here
return Scaffold(
appBar: AppBar(
More about StatefulWidget

Why is the refreshing pull in the App not working?

I'm building my app with Flutter 2.10.5 and Dart 2.16.2.
When i try to refresh the demo content whith a pull, nothing happens. I have multiple navigation routes for different content. So the demo is a litte bit complex.
The main.dart includes the basic code for the app. I use the NavDrawer Widget to build the different pages. Every route is defined in the navigation.dart file, which reference to the content widgets.
My code so far is:
import 'dart:core';
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of the application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo Company',
theme: ThemeData(),
debugShowCheckedModeBanner: false,
home: const HomePage(title: 'Demo Company'),
);
}
}
class _HomePageState extends State<HomePage> {
#override
initState() {
super.initState();
}
Widget _infoTile(String title, String subtitle) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle.isEmpty ? 'Not set' : subtitle),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: <Widget>[
_infoTile('App name', 'Demo App....'),
// Multiple Liste Tiles...
],
),
),
);
}
}
//----------------------------------------------------------------------
// navigation.dart
//----------------------------------------------------------------------
class NavDrawer extends StatelessWidget {
const NavDrawer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text(
'Navigation',
style: TextStyle(color: Colors.white, fontSize: 30),
),
SizedBox(height: 30.0),
Text('Firstname', style: TextStyle(color: Colors.black, fontSize: 15)),
Text('Accountname', style: TextStyle(color: Colors.black, fontSize: 15)),
],
),
),
ListTile(
leading: const Icon(Icons.notifications),
title: const Text('Demo'),
onTap: () {
Navigator.push(
context,
Demo.route(),
);
},
),
// Multiple Navigation List Tiles...
],
),
);
}
}
//----------------------------------------------------------------------
// demo.dart
//----------------------------------------------------------------------
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<HomePage> createState() => _HomePageState();
}
class Demo extends StatefulWidget {
const Demo({Key? key}) : super(key: key);
static Route route() {
return CupertinoPageRoute(builder: (_) => const Demo());
}
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final _data = <WordPair>[];
#override
void initState() {
super.initState();
_data.addAll(generateWordPairs().take(20));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
),
body: _buildList(),
);
}
Widget _buildList() {
return RefreshIndicator(
onRefresh: _refreshData,
child: ListView.builder(
padding: const EdgeInsets.all(20.0),
itemBuilder: (context, index) {
WordPair wordPair = _data[index];
return _buildListItem(wordPair.asString, context);
},
itemCount: _data.length,
),
);
}
Widget _buildListItem(String word, BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(word),
),
);
}
Future _refreshData() async {
await Future.delayed(const Duration(seconds: 3));
_data.clear();
_data.addAll(generateWordPairs().take(20));
setState(() {});
}
}
class ShowMessages extends StatelessWidget {
final String type;
final Color color;
const ShowMessages({Key? key, required this.type, required this.color}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
//color: color,
physics: const AlwaysScrollableScrollPhysics(),
children: [
ListTile(
title: Text(
type,
style: Theme.of(context).textTheme.bodyText1,
),
),
]);
}
}
Copy this code to DartPad
What is wrong?
Well for me this code... works
I copied it into Dartpad, then Dev Tools in browser (F12) > Device Emulation > Responsive. And you can use pull to refresh.
Of course this doesn't work using web view and mouse. I believe this gesture is not supported.

What argument should i give?

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),

Bounce Dismissible for helping the user

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')),
),
),
),
),
],
),
),
);
}
}