How to upload a video from gallery in flutter - flutter

I am building an app when a user clicks the button, it goes to gallery and select any of the video in gallery and then returns back to the main screen in app and plays the video automatically. Below is code i have tried.
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Image App Demo',
theme: ThemeData(
primaryColor: Color(0xff476cfb),
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File _imageFile;
Future getVideo() async{
File image;
image=await ImagePicker.pickVideo(source: ImageSource.gallery);
setState(() {
_imageFile=image;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Image Upload"),
),
body: ListView(
children: <Widget>[
Center(
child: Column(
children: <Widget>[
SizedBox(height: 10.0,),
RaisedButton(
child: Text("Video"),
onPressed: (){
getVideo();
},
),
],
),
)
],
}

Display video using video player
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:video_player/video_player.dart';
void main() => runApp(VideoPlayerApp());
class VideoPlayerApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Video Player Demo',
home: VideoPlayerScreen(),
);
}
}
class VideoPlayerScreen extends StatefulWidget {
VideoPlayerScreen({Key key}) : super(key: key);
#override
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
VideoPlayerController _controller;
Future<void> _initializeVideoPlayerFuture;
File videoFile;
#override
void initState() {
// Create and store the VideoPlayerController. The VideoPlayerController
// offers several different constructors to play videos from assets, files,
super.initState();
}
#override
void dispose() {
// Ensure disposing of the VideoPlayerController to free up resources.
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Butterfly Video'),
),
// Use a FutureBuilder to display a loading spinner while waiting for the
// VideoPlayerController to finish initializing.
body: Column(
children: <Widget>[
Visibility(
visible: _controller != null,
child: FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_controller),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
),
RaisedButton(
child: Text("Video"),
onPressed: () {
getVideo();
},
),
],
),
floatingActionButton: _controller == null
? null
: FloatingActionButton(
onPressed: () {
// Wrap the play or pause in a call to `setState`. This ensures the
// correct icon is shown.
setState(() {
// If the video is playing, pause it.
if (_controller.value.isPlaying) {
_controller.pause();
} else {
// If the video is paused, play it.
_controller.play();
}
});
},
// Display the correct icon depending on the state of the player.
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Future getVideo() async {
Future<File> _videoFile =
ImagePicker.pickVideo(source: ImageSource.gallery);
_videoFile.then((file) async {
setState(() {
videoFile = file;
_controller = VideoPlayerController.file(videoFile);
// Initialize the controller and store the Future for later use.
_initializeVideoPlayerFuture = _controller.initialize();
// Use the controller to loop the video.
_controller.setLooping(true);
});
});
}
}

you can use this widget from image_picker
answer based n image_picker example
Widget _previewVideo() {
final Text retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_controller == null) {
return const Text(
'You have not yet picked a video',
textAlign: TextAlign.center,
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatioVideo(_controller),
);
}
//how to pass video to preview
Center(
child: Platform.isAndroid
? FutureBuilder<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return isVideo ? _previewVideo() : _previewImage();
default:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: (isVideo ? _previewVideo() : _previewImage()),
),

Related

Unable to display video in flutter without overflow using video_player package

I'm having a hard time displaying a video using the video_player package. I'm new to flutter and am trying to create a simple website with a title, some text, and a local video displayed in the center of the screen with a title. The video is local and is in an assets folder, and I have also added the video_player and video location to the pubspec.yaml file. Any advice or suggestions are appreciated, I would like to improve at this language. Below is the code I have put together so far. Thanks!
import 'package:video_player/video_player.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text(
"My Website",
textScaleFactor: 2,
style: TextStyle(color: Colors.white),
),
),
),
body: Column(
children: [
MyHomePage(),
],
),
),
);
}
}
MyHomePage class:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Expanded(
/*1*/
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/*2*/
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Center(
child: Text(
'More photos & videos coming soon',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
),
Container(
padding: const EdgeInsets.only(bottom: 8),
child: Center(child: BackgroundVideo()),
),
],
),
),
],
),
);
}
}
Stateful video player that I would like to embed in the home page:
//need to ensure this video fits on the screen and doesn't overflow the bottom.
class BackgroundVideo extends StatefulWidget {
const BackgroundVideo({super.key});
#override
_BackgroundVideoState createState() => _BackgroundVideoState();
}
class _BackgroundVideoState extends State<BackgroundVideo> {
late VideoPlayerController _controller;
late Future<void> _initializeBackgroundVideoFuture;
#override
void initState() {
super.initState();
//Create and store the BackgroundVideoController.
_controller = VideoPlayerController.asset(
"build\assets\welcomescreen.mp4",
);
_initializeBackgroundVideoFuture = _controller.initialize();
_controller.setLooping(true);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('First Video'),
),
// Use a FutureBuilder to display a loading spinner while waiting for the
// VideoPlayerController to finish initializing.
body: FutureBuilder(
future: _initializeBackgroundVideoFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: VideoPlayer(_controller),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Wrap the play or pause in a call to `setState`. This ensures the
// correct icon is shown.
setState(() {
// If the video is playing, pause it.
if (_controller.value.isPlaying) {
_controller.pause();
} else {
// If the video is paused, play it.
_controller.play();
}
});
},
// Display the correct icon depending on the state of the player.
child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
}

How to temporarily unsubscribe to Stream after Navigator.of(context).push(....)?

Scenario : There are two pages. PhonePage and OtpPage . User inputs phone number in PhonePage and gets redirected to OtpPage to verify the OTP sent to him.
Problem: The API that talks to the server uses a StreamController.broadcast() to tell the app the response of the request. This Stream is shared by both PhonePage and OtpPage and produces events. The two pages listen to the stream and decide what to do based on the event.
However, after Navigator.push(), the old page is still listening to the stream. Thus when user in OtpPage taps the resend button, Navigator.push in PhonePage is still called although it should not.
Question What does Flutter has to deal with such scenario ? I tried onDispose() but it does not get called.
I would appreciate if you could also explain why onDispose is not called too.
Code: this is the code to reproduce the scenario. You can just paste it on your IDE or DartPad https://dartpad.dev/flutter (Note: When you go to OtpPage and some text on the text field, the click on resend button. Notice how a new OtpPage Widget is added on top the Navigator tree. That's the unwanted behaviour )
import 'dart:async';
import 'package:flutter/material.dart';
final fakeApiResponse = StreamController.broadcast();
void main() => runApp(MyApp(),);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PhoneNumber(),
);
}
}
class PhoneNumber extends StatefulWidget {
#override
_PhoneNumberState createState() => _PhoneNumberState();
}
class _PhoneNumberState extends State<PhoneNumber> {
StreamSubscription apiEventListner;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Enter your phone number'),
RaisedButton(
child: Text('Send OTP'),
onPressed: () {
fakeApiResponse.add('OTP Sent');
},
),
],
),
);
}
#override
void initState() {
super.initState();
apiEventListner = fakeApiResponse.stream.listen((data) {
if (data == 'OTP Sent') {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => VerifyOtp(),
),
);
}
});
}
#override
void dispose() {
super.dispose();
apiEventListner.cancel();
}
}
class VerifyOtp extends StatefulWidget {
#override
_VerifyOtpState createState() => _VerifyOtpState();
}
class _VerifyOtpState extends State<VerifyOtp> {
StreamSubscription apiEventListner;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(
decoration: InputDecoration(hintText: 'Enter OTP Here'),
),
RaisedButton(
child: Text("Verify"),
onPressed: () {
fakeApiResponse.add('OTP Verified');
},
),
RaisedButton(
child: Text("Didn't get the code? Resend OTP"),
onPressed: () {
fakeApiResponse.add('OTP Sent');
},
),
],
),
);
}
#override
void initState() {
super.initState();
apiEventListner = fakeApiResponse.stream.listen((data) {
if (data == 'OTP Sent') {
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("OTP Resent"),
content: Text("Enter new OTP"),
);
},
);
}else if (data == 'OTP Verified'){
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>SuccessPage()));
}
});
}
#override
void dispose() {
super.dispose();
apiEventListner.cancel();
}
}
class SuccessPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('SUCCESS!'),
),
);
}
}
I found the solution. I am going to post anyways it here in case it might be helpful for someone else.
The trick is to detect the current active route and return if the current widget is not the current route inside the listeners of the Streams.
Flutter has an API called ModalRoute route = ModalRoute.of(context); then route.isCurrent will be true if the current widget is the current route.
Then you have to add this check in both pages.
The final working code would be:
import 'dart:async';
import 'package:flutter/material.dart';
final fakeApiResponse = StreamController.broadcast();
void main() => runApp(
MyApp(),
);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PhoneNumber(),
);
}
}
class PhoneNumber extends StatefulWidget {
#override
_PhoneNumberState createState() => _PhoneNumberState();
}
class _PhoneNumberState extends State<PhoneNumber> {
StreamSubscription apiEventListner;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Enter your phone number'),
RaisedButton(
child: Text('Send OTP'),
onPressed: () {
fakeApiResponse.add('OTP Sent');
},
),
],
),
);
}
#override
void initState() {
super.initState();
apiEventListner = fakeApiResponse.stream.listen((data) {
ModalRoute route = ModalRoute.of(context);
String name = route?.settings?.name;
print("Phone page isCurrent: ${route?.isCurrent} isFirst: ${route?.isFirst} active: ${route?.isActive} $name");
if (route?.isCurrent != null && !route.isCurrent) {
return;
}
if (data == 'OTP Sent') {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => VerifyOtp(),
),
);
}
});
}
#override
void dispose() {
super.dispose();
apiEventListner.cancel();
}
}
class VerifyOtp extends StatefulWidget {
#override
_VerifyOtpState createState() => _VerifyOtpState();
}
class _VerifyOtpState extends State<VerifyOtp> {
StreamSubscription apiEventListner;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(
decoration: InputDecoration(hintText: 'Enter OTP Here'),
),
RaisedButton(
child: Text("Verify"),
onPressed: () {
fakeApiResponse.add('OTP Verified');
},
),
RaisedButton(
child: Text("Didn't get the code? Resend OTP"),
onPressed: () {
fakeApiResponse.add('OTP Sent');
},
),
],
),
);
}
#override
void initState() {
super.initState();
apiEventListner = fakeApiResponse.stream.listen((data) {
ModalRoute route = ModalRoute.of(context);
String name = route?.settings?.name;
print("OTP page isCurrent: ${route?.isCurrent} isFirst: ${route?.isFirst} active: ${route?.isActive} $name");
if (route?.isCurrent != null && !route.isCurrent) {
return;
}
if (data == 'OTP Sent') {
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("OTP Resent"),
content: Text("Enter new OTP"),
);
},
);
} else if (data == 'OTP Verified') {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SuccessPage()));
}
});
}
#override
void dispose() {
super.dispose();
apiEventListner.cancel();
}
}
class SuccessPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('SUCCESS!'),
),
);
}
}

What is the proper way of using SharedPreferences with Provider in Flutter?

I am newbie to state management using provider in flutter.
I've created a model named as Counter:
import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int value = 0;
void increment() {
value++;
notifyListeners();
}
void decrement() {
value--;
notifyListeners();
}
}
Now when value changes I can save it locally using SharedPreferences in order to start from that value next time.
But, I do not know what would be a proper way of loading data from local and set value in Counter class.
Should I load saved data in main.dart file when app is initalized and then setValue to that data?
Or are there any solutions, for example, loading data directly in my Counter class?
create a SharedPreferencesProvider
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesProvider {
final Future<SharedPreferences> sharedPreferences;
SharedPreferencesProvider(this.sharedPreferences);
Stream<SharedPreferences> get prefsState => sharedPreferences.asStream();
}
then create a Provider and with a StreamProvider as shown below
return MultiProvider(
providers: [
Provider<SharedPreferencesProvider>(create: (_) => SharedPreferencesProvider(SharedPreferences.getInstance())),
StreamProvider(create: (context) => context.read<SharedPreferencesProvider>().prefsState, initialData: null)
then consume the state within a Widget build with a context.watch
#override
Widget build(BuildContext context) {
sharedPrefs = context.watch<SharedPreferences>();
Try to use the future builder and then set it to the provider and be able to use SharedPreferences everywhere in the app:
#override
Widget build(BuildContext context) {
return FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data != null) {
return MultiProvider(providers: [
Provider<SharedPreferences>(
create: (context) => snapshot.data!,
),
],
);
}
},
);
}
And you can use context.read() everywhere.
The question is leaning toward opinion. I'm also new to flutter -- the below may not be the best way, but it does work, so maybe it will help someone.
If it's the top level app, you can initialize the counter before actually using it, displaying a loading page during the load time (imperceptible in this case). You must include the first runApp however, otherwise shared_preferences will not be able to correctly access the file containing these preferences on the device.
A similar thing can be done with with FutureBuilder, but you must await a delay prior to attempting to read from shared_preferences.
(I don't think the loading page or delay are necessary if you aren't using the widget as your top level widget, which would probably be better anyway. In that case, probably FutureBuilder would be the correct solution. (?))
To note:
I added an async "constructor" to the Counter class that initializes from the shared_preferences.
I access the Counter via provider library in _MyHomePageState.build with context.watch<Counter>(), which causes this to rebuild on changes (without requiring calls to setState.
I've added async Counter._updatePreferences which is called in Counter.increment and Counter.decrement, which saves the current value of the Counter to the shared_preferences.
Imports and main for first method
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
// run "loading" app while awaiting counter, then run app again
runApp(
const MaterialApp(
home: Center(
child: Text('Loading'),
),
)
);
final Counter counter = await Counter.fromPreferences();
runApp(
ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
)
);
}
Imports and main (with FutureBuilder)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
// Get counter in future builder
runApp(
FutureBuilder<Counter>(
future: Counter.fromPreferences(),
builder: (BuildContext context, AsyncSnapshot<Counter> snapshot) {
Widget returnWidget = const MaterialApp(
home: Center(
child: Text('Loading'),
),
);
if (snapshot.connectionState == ConnectionState.waiting) {
} else if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
print(snapshot.error);
} else if (snapshot.hasData) {
final Counter counter = snapshot.data!;
returnWidget = ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
);
} else {
print('No data');
}
} else if (snapshot.connectionState == ConnectionState.none) {
print('null future');
} else {
print(snapshot.connectionState);
}
return returnWidget;
},
),
);
}
MyApp and MyHomePage
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Counter App',
home: MyHomePage(title: 'Counter App Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final Counter counter = context.watch<Counter>();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <FloatingActionButton>[
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
],
),
);
}
}
Counter Class (ChangeNotifier)
class Counter extends ChangeNotifier {
int value = 0;
static Future<Counter> fromPreferences() async {
final Counter counter = Counter();
// Must be included if using the FutureBuilder
// await Future<void>.delayed(Duration.zero, () {});
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int value = prefs.getInt('counterValue') ?? 0;
counter.value = value;
return counter;
}
Future<void> _updatePreferences() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('counterValue', value);
}
void increment() {
value++;
notifyListeners();
_updatePreferences();
}
void decrement() {
value--;
notifyListeners();
_updatePreferences();
}
}
Complete Example
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
// run "loading" app while awaiting counter, then run app again
runApp(
const MaterialApp(
home: Center(
child: Text('Loading'),
),
)
);
final Counter counter = await Counter.fromPreferences();
runApp(
ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
)
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Counter App',
home: MyHomePage(title: 'Counter App Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final Counter counter = context.watch<Counter>();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <FloatingActionButton>[
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
],
),
);
}
}
class Counter extends ChangeNotifier {
int value = 0;
static Future<Counter> fromPreferences() async {
final Counter counter = Counter();
// Must be included if using the FutureBuilder
// await Future<void>.delayed(Duration.zero, () {});
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int value = prefs.getInt('counterValue') ?? 0;
counter.value = value;
return counter;
}
Future<void> _updatePreferences() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('counterValue', value);
}
void increment() {
value++;
notifyListeners();
_updatePreferences();
}
void decrement() {
value--;
notifyListeners();
_updatePreferences();
}
}
Use the shared_preferences plugin
enter link description here
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SharedPreferences Demo',
home: SharedPreferencesDemo(),
);
}
}
class SharedPreferencesDemo extends StatefulWidget {
SharedPreferencesDemo({Key key}) : super(key: key);
#override
SharedPreferencesDemoState createState() => SharedPreferencesDemoState();
}
class SharedPreferencesDemoState extends State<SharedPreferencesDemo> {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<int> _counter;
Future<void> _incrementCounter() async {
final SharedPreferences prefs = await _prefs;
final int counter = (prefs.getInt('counter') ?? 0) + 1;
setState(() {
_counter = prefs.setInt("counter", counter).then((bool success) {
return counter;
});
});
}
#override
void initState() {
super.initState();
_counter = _prefs.then((SharedPreferences prefs) {
return (prefs.getInt('counter') ?? 0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("SharedPreferences Demo"),
),
body: Center(
child: FutureBuilder<int>(
future: _counter,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const CircularProgressIndicator();
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text(
'Button tapped ${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n\n'
'This should persist across restarts.',
);
}
}
})),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Reference site : https://pub.dev/packages/shared_preferences#-example-tab-

Switch toggle between time format (am/pm and 24H) from settings page

i want to have switch widget in settings page. i can figured switching theme using switch widget, but this is too complicated
my main_page:
import 'package:intl/intl.dart';
Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Text("Summary"),
),
SizedBox(height: 45),
_time24(snapshot), //show this if switch is off
_time12(snapshot), //show this if switch is on
],
)
Widget _time12(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(Icons.work, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(Icons.restaurant, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Thank you for your time :)
You can copy paste run full code below
You can await Navigator.push then call setState()
code snippet
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
}
...
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:intl/intl.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 Payload {
String home;
String work;
String restaurant;
Payload({this.home, this.work, this.restaurant});
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Future _future;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget _time12(AsyncSnapshot snapshot) {
var a = DateFormat("HH:mm").parse(snapshot.data.home);
print("a ${a}");
return Column(children: <Widget>[
_time(
Icons.home,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(
Icons.work,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(
Icons.restaurant,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot) {
return Column(children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Widget _time(IconData iconData, Text _text) {
return ListTile(
leading: Icon(iconData),
title: _text,
);
}
Future<Payload> getData() {
print("getData");
return Future.value(
Payload(home: "18:00", restaurant: "13:00", work: "08:00"));
}
#override
void initState() {
// TODO: implement initState
_future = getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<Payload>(
future: _future,
builder: (BuildContext context, AsyncSnapshot<Payload> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
}
return null; // unreachable
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
bool is24 = true;
class Setting extends StatefulWidget {
#override
_SettingState createState() => _SettingState();
}
class _SettingState extends State<Setting> {
void _changed(value) {
setState(() {
is24 = value;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Container(
child: Switch(
value: is24,
onChanged: _changed,
)));
}
}

Change size/position of Text relative to other Widget

Can anyone help?
Currently, the Text that I am displaying over a video has a fixed size and position.
I'm wondering how I can change this dynamically/responsively to match the size of its parent widget (the Video).
I tried a method using a GlobalKey but got an error, I think it's because the video hadn't loaded..
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
drawer: ResponsiveLayout.isSmallScreen(context) ? NavDrawer() : null,
body: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
NavBar(),
Body(),
Footer(),
],
),
),
),
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ResponsiveLayout(
largeScreen: LargeScreen(),
mediumScreen: LargeScreen(),
smallScreen: LargeScreen(),
);
}
}
class LargeScreen extends StatefulWidget {
#override
_LargeScreenState createState() => _LargeScreenState();
}
class _LargeScreenState extends State<LargeScreen> {
VideoPlayerController _videoPlayerController;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_videoPlayerController = VideoPlayerController.asset(
'assets/videos/video.mp4',
);
_initializeVideoPlayerFuture = _videoPlayerController.initialize();
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
!_videoPlayerController.value.isBuffering) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the VideoPlayer.
return AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: Stack(
children: <Widget>[
VideoPlayer(_videoPlayerController),
Positioned(
bottom: 20,
left: 20,
child: FittedBox(
child: Text(
'Text over\na video',
style: TextStyle(
color: Colors.white,
fontSize:50),
),
),
)
],
),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
],
),
);
}
#override
void dispose() {
super.dispose();
_videoPlayerController.dispose();
}
}
LayoutBuilder can provide you with width and height properties which corresponds to the currently available space. Check this documentation here. It provides the builder with a BoxConstraints instance as in here. You can use this information to size your text.
Check the Align widget. It can place the child on specific location within the parent widget's coordinate system. In your case it will be on the coordinates of the Stack widget.
I would try something like the following.
Wrap the Text widget inside a Align widget and use FractionalOffset to place align the widget. You can directly use the Alignment instance also. The origin will vary vary for both the approach. Check the docs here
Then Wrap my Align widget inside a LayoutBuilder widget to get the available size and decide my font size based on it. Something like fontSize: constraints.maxWidth / 25
Below is sample working code.
// 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';
import 'package:video_player/video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
// drawer: ResponsiveLayout.isSmallScreen(context) ? NavDrawer() : null,
body: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
// NavBar(),
Body(),
// Footer(),
],
),
),
),
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
// return ResponsiveLayout(
// largeScreen: LargeScreen(),
// mediumScreen: LargeScreen(),
// smallScreen: LargeScreen(),
// );
return LargeScreen();
}
}
class LargeScreen extends StatefulWidget {
#override
_LargeScreenState createState() => _LargeScreenState();
}
class _LargeScreenState extends State<LargeScreen> {
VideoPlayerController _videoPlayerController;
Future<void> _initializeVideoPlayerFuture;
#override
void initState() {
_videoPlayerController = VideoPlayerController.network(
'http://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
);
_initializeVideoPlayerFuture =
_videoPlayerController.initialize().then((onValue) {
setState(() {});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
children: <Widget>[
FutureBuilder(
future: _initializeVideoPlayerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
!_videoPlayerController.value.isBuffering) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the VideoPlayer.
return AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: Stack(
children: <Widget>[
VideoPlayer(_videoPlayerController),
LayoutBuilder(
builder: (context, constraints) {
return Align(
// this decides the position of the text.
alignment: FractionalOffset(0.05, 0.95),
child: FittedBox(
child: Text(
'Text over\na video',
style: TextStyle(
color: Colors.white,
// here font size is ratio of the maxwidth available for this widget.
fontSize: constraints.maxWidth / 25,
),
),
),
);
},
)
],
),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
),
FloatingActionButton(
onPressed: () {
setState(() {
_videoPlayerController.value.isPlaying
? _videoPlayerController.pause()
: _videoPlayerController.play();
});
},
child: Icon(
_videoPlayerController.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
),
],
),
);
}
#override
void dispose() {
super.dispose();
_videoPlayerController.dispose();
}
}
It's easily accessible via MediaQuery.of(context).size (Documentation).
Remember that you have to call inside your build method since it demands the context