Flutter DorpDownMenu selected item don't change - flutter

in this below code when i try change item on DropDownMenu, selected item don't change and selected item is first item of list
for example after selecting bbbbbbb i have aaaaaaa or selecting ccccccc i have aaaaaaa
import 'package:flutter/material.dart';
void main()=>runApp(
MaterialApp(
home: _MyApp(),
),
);
class _MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState()=>_MyAppState();
}
class _MyAppState extends State<_MyApp> {
SessionsEntity sessionData;
#override
Widget build(BuildContext context) {
List<DropdownMenuItem<SessionsEntity>> _dropdownMenuItems;
_dropdownMenuItems = buildDropdownMenuItems();
sessionData = _dropdownMenuItems[0].value;
return Scaffold(
body: DropdownButtonHideUnderline(
child: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.white,
),
child: Container(
child: Center(
child: DropdownButton(
items: _dropdownMenuItems,
isDense: true,
value: sessionData,
onChanged: onChangeDropdownItem,
isExpanded: true,
hint: Text('please select item'),
),
),
),
),
),
);
}
List<DropdownMenuItem<SessionsEntity>> buildDropdownMenuItems() {
List<SessionsEntity> sessions = [
SessionsEntity(1, 'aaaaaaa', 1, 'a-a-a-a-a'),
SessionsEntity(2, 'bbbbbbb', 2, 'b-b-b-b-b'),
SessionsEntity(3, 'ccccccc', 2, 'c-c-c-c-c'),
];
List<DropdownMenuItem<SessionsEntity>> items = List();
for (SessionsEntity session in sessions) {
items.add(
DropdownMenuItem(
value: session,
child: Text(session.sessionName),
),
);
}
return items;
}
onChangeDropdownItem(SessionsEntity selectedSession) {
setState(() {
sessionData = selectedSession;
});
}
}
class SessionsEntity {
final int id;
String sessionName;
int sessionType;
String dateTime;
SessionsEntity(this.id, this.sessionName,this.sessionType, this.dateTime);
}

The problem lies at the below line.
sessionData = _dropdownMenuItems[0].value;
Since your are initializing sessionData variable every time in the build() method, the call to setState() has no effect.
Basically setState() calls the build() method again with the new data but when you initialize sessionData insiside build() it gets overwritten with same value every time build() is called.
You should declare the use the _dropdownMenuItems list as a class variable instead of inside build() and use the initState() method to initialize the _dropdownMenuItems and sessionData variables only once. Here is an example -
class _MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState()=>_MyAppState();
}
class _MyAppState extends State<_MyApp> {
//Remove below line from build method
List<DropdownMenuItem<SessionsEntity>> _dropdownMenuItems;
SessionsEntity sessionData;
#override
void initState() {
super.initState();
_dropdownMenuItems = buildDropdownMenuItems();
sessionData = _dropdownMenuItems[0].value;
}
#override
Widget build(BuildContext context) {
//Rest of the code remains same
}
}

Following will help you. The sample widget is made for selecting a reason.
Sample widget code :
Widget dropdownReasons() {
return new DropdownButton<String>(
value: _currentReason,
items: _dropDownMenuItems,
onChanged: changedDropDownItem);
}
}
The function below handles what to do when the drop down value is changed :
void changedDropDownItem(String selectedReason) {
setState(() => _currentReason = selectedReason; // <-- This is the most important line
}

Try below code, this will work fine
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: new MyApp(),),
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
SessionsEntity sessionData;
List<DropdownMenuItem<SessionsEntity>> _dropdownMenuItems;
// Build method is called whenever there is change in the state or ui, so instead of initializing the dropdown menu items and initial selected value of dropdown menu in build method you should initialize it in initState method which is called only once.
#override
void initState() {
// TODO: implement initState
_dropdownMenuItems = buildDropdownMenuItems();
sessionData = _dropdownMenuItems[0].value;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: DropdownButtonHideUnderline(
child: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.white,
),
child: Container(
child: Center(
child: DropdownButton(
items: _dropdownMenuItems,
isDense: true,
value: sessionData,
onChanged: onChangeDropdownItem,
isExpanded: true,
hint: Text('please select item'),
),
),
),
),
),
);
}
List<DropdownMenuItem<SessionsEntity>> buildDropdownMenuItems() {
List<SessionsEntity> sessions = [
SessionsEntity(1, 'aaaaaaa', 1, 'a-a-a-a-a'),
SessionsEntity(2, 'bbbbbbb', 2, 'b-b-b-b-b'),
SessionsEntity(3, 'ccccccc', 2, 'c-c-c-c-c'),
];
List<DropdownMenuItem<SessionsEntity>> items = List();
for (SessionsEntity session in sessions) {
items.add(
DropdownMenuItem(
value: session,
child: Text(session.sessionName),
),
);
}
return items;
}
onChangeDropdownItem(SessionsEntity selectedSession) {
setState(() {
sessionData = selectedSession;
});
}
}
class SessionsEntity {
final int id;
String sessionName;
int sessionType;
String dateTime;
SessionsEntity(this.id, this.sessionName,this.sessionType, this.dateTime);
}

Related

How can I dump data into more than 2 textfields with one suggestion? [Flutter]

I want to autofill several textfields with one suggestion, like for example: If I select Washington as a state where I live I want the other field that would be country field to fill itself with U.S.
Thanks for your attention!
You will need to use setState( ) inside the onChanged. inside that setState, you will change the value of the other field otherDropdownValue . Here is a small example with dropDownMenus.
Dont forget you need a StatefulWidget (not StateLess)
Code:
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String dropdownValue = 'One';
String otherDropdownValue = 'Two';
#override Widget build(BuildContext context) {
return Column(children: [
DropdownButton<String>(
value: dropdownValue,
onChanged: (String? newValue) {
//******************************************
//*****Here is what you are looking for*****
//******************************************
setState(() {
dropdownValue = newValue;
otherDropdownValue = newValue; ///Changes the other one
});
},
items: <String>['One', 'Two', 'Free', 'Four'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(value: value, child: Text(value),);}).toList(),
),
DropdownButton<String>(
value: otherDropdownValue,
onChanged: (String? newValue) {
setState(() {
otherDropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(value: value, child: Text(value),);}).toList(),
),
],
);
}
}
Let me know if this does not help?
EDIT to answer your last comment:
Same logic to apply with a TextField or a textformfield.
You will need to add a TextEditingController() to control the text displayed.
Below is a fully working example (the part you need to look at is at the end)
and here is a link that explains the code (note I adjusted the code for your specific use case)
https://flutter.dev/docs/cookbook/forms/text-field-changes
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: 'Retrieve Text Input',
home: MyCustomForm(),
);
}
}
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final myController = TextEditingController();
#override
void initState() {
super.initState();
// Start listening to changes.
myController.addListener(_printLatestValue);
}
#override
void dispose() {
// Clean up the controller when the widget is removed from the widget tree.
// This also removes the _printLatestValue listener.
myController.dispose();
super.dispose();
}
void _printLatestValue() {
print('Second text field: ${myController.text}');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
///********************
///**** LOOK HERE ****
///********************
TextField(
onChanged: (text) {
myController.text = text;
},
),
TextField(
controller: myController,
),
],
),
),
);
}
}

Implement setstat and bind variables

I'm trying to get and display 2 variable values from another dart file, ("int myId" and "String myMenu") , these variables are updated with every "onTap" widget, my code works, but only if i do a "hot reload", i think that i need to put a "setstate" somewhere, but i'm having difficulty to implement it.
I think the problem is there, my widget text returns "null" to me, but if I hit the menu button and do a "hot reload", it's ok.
displayText.dart
import 'package:flutter/material.dart';
import './menu.dart';
class display extends StatefulWidget {
int myId;
String myMenu;
display(this.myId, this.myMenu);
#override
_displayState createState() => _displayState();
}
class _displayState extends State<display> {
Future myVarUsed() async {
//Each press on the button return the value
setState(() {
print('myIdDsiplay: ${widget.myId}'); // null
print('myMenuDisplay : ${widget.myMenu}'); // null
});
}
#override
void initState() {
super.initState();
myVarUsed();
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 250,
width: 250,
child: Row(
children: [
Text('My ID is : ${widget.myId}'),
Text('My menu is : ${widget.myMenu}'),
],
),
);
}
}
This file contains the menu inside a scrollbar, each button return the ID and the name (of the button) and store it in 2 variable ("int myId" and "String myMenu") that i want to pass.
menu.dart
import 'package:flutter/material.dart';
import './mylist.dart';
import './displayText.dart';
class Menu extends StatefulWidget {
static int myId;
static String myMenu;
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Container scrollList() {
final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);
return Container(
color: Colors.red,
height: 90,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: listdata.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Container(
child: gestureDetector_Ontap(index),
);
},
),
);
}
GestureDetector gestureDetector_Ontap(int index) {
return GestureDetector(
onTap: () {
Menu.myId = listdata[index].id;
Menu.myMenu = listdata[index].menuObj;
display(Menu.myId, Menu.myMenu);
print('myIDMenu ${Menu.myId}');
print('myMenuMenu ${Menu.myMenu}');
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'${listdata[index].menuObj}',
),
),
);
}
Widget build(BuildContext context) {
return Container(
child: scrollList(),
);
}
}
This file contains my list and his class
mylist.dart
class listModel {
int id;
String menuObj;
listModel(this.id, this.menuObj);
}
List listdata = [
listModel(0, 'Menu01'),
listModel(1, 'Menu02'),
listModel(2, 'Menu03'),
listModel(3, 'Menu04'),
listModel(4, 'Menu05')
];
And the container
main.dart
import 'package:flutter/material.dart';
import './menu.dart';
import './displayText.dart';
import './mylist.dart';
void main() {
runApp(MyHomePage());
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Column(
children: <Widget>[
Menu(),
display(Menu.myId, Menu.myMenu),
],
),
),
),
);
}
}
The problem
You're defining Menu this way:
class Menu extends StatefulWidget {
static int myId;
static String myMenu;
#override
_MenuState createState() => _MenuState();
}
When your app starts, myId and myMenu are uninitialized variables, therefore they're implicitely set to null.
Inside _MyHomePageState, you call
display(Menu.myId, Menu.myMenu)
Since you haven't initialized Menu.myId and Menu.myMenu yet, they're still null.
When you tap the GestureDetector, you initialize Menu.myId and Menu.myMenu this way:
Menu.myId = listdata[index].id;
Menu.myMenu = listdata[index].menuObj;
display(Menu.myId, Menu.myMenu);
print('myIDMenu ${Menu.myId}');
print('myMenuMenu ${Menu.myMenu}');
Now, Menu.myId and Menu.myMenu are defined to non-null values. However, this will not update the Container's display(Menu.myId, Menu.myMenu), so they'll still be null, you need to update it by yourself.
The solution
I've added comments through the code, pointing a better approach:
import 'package:flutter/material.dart';
// Avoid displaying the warning "Name types using UpperCamelCase."
class Display extends StatefulWidget {
// Make these fields final and the constructor const
final int myId;
final String myMenu;
const Display(this.myId, this.myMenu);
#override
_DisplayState createState() => _DisplayState();
}
// Avoid displaying the warning "Name types using UpperCamelCase."
class _DisplayState extends State<Display> {
// You don't need this Future nor this initState
//
// Future myVarUsed() async {
// setState(() {
// print('myIdDsiplay: ${widget.myId}'); // null
// print('myMenuDisplay : ${widget.myMenu}'); // null
// });
// }
//
// #override
// void initState() {
// super.initState();
// myVarUsed();
// }
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
height: 250,
width: 250,
child: Row(
children: [
Text('My ID is : ${widget.myId}'),
Text('My menu is : ${widget.myMenu}'),
],
),
);
}
}
class Menu extends StatefulWidget {
// Avoid using mutable static fields
// static int myId;
// static String myMenu;
// To simplify, you can add a onChanged callback to
// be triggered whenever you change `myId` and `myMenu`
final void Function(int myId, String myMenu) onChanged;
const Menu({this.onChanged});
#override
_MenuState createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Container scrollList() {
final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);
return Container(
color: Colors.red,
height: 90,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: listdata.length,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Container(
child: gestureDetectorOntap(index),
);
},
),
);
}
// Avoid displaying the warning "Name non-constant identifiers using lowerCamelCase."
GestureDetector gestureDetectorOntap(int index) {
return GestureDetector(
onTap: () {
// Make these local variables
int myId = listdata[index].id;
String myMenu = listdata[index].menuObj;
// Call the `onChanged` callback
widget.onChanged(myId, myMenu);
// This widget is being thrown away
// display(Menu.myId, Menu.myMenu);
print('myIDMenu $myId');
print('myMenuMenu $myMenu');
},
child: Container(
alignment: AlignmentDirectional.center,
child: Text(
'${listdata[index].menuObj}',
),
),
);
}
Widget build(BuildContext context) {
return Container(
child: scrollList(),
);
}
}
// Avoid the warning "Name types using UpperCamelCase."
class ListModel {
// You can make these fields final and the constructor const
final int id;
final String menuObj;
const ListModel(this.id, this.menuObj);
}
// You can make this list const to avoid modifying it unintentionally later
const List<ListModel> listdata = [
ListModel(0, 'Menu01'),
ListModel(1, 'Menu02'),
ListModel(2, 'Menu03'),
ListModel(3, 'Menu04'),
ListModel(4, 'Menu05')
];
void main() {
runApp(MyHomePage());
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Create fields to store the current `myId` and current `myMenu`
int myId;
String myMenu;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Container(
child: Column(
children: <Widget>[
// Add the `onChanged` callback here, updating this widget state
Menu(
onChanged: (newMyId, newMyMenu) {
setState(() {
myId = newMyId;
myMenu = newMyMenu;
});
}
),
// Access the current values here
Display(myId, myMenu),
],
),
),
),
);
}
}

Flutter DropdownButtonFormField not updating variable

So I am struggling with the DropdownButtonFormField where when you change the value it runs the onChange function with the updated value. However, once the onChange finishes the value variable seems to reset itself meaning it never changes.
This is a cut-down version of the full form:
final _formKey = GlobalKey<FormState>();
TextEditingController assetGroupNameController = new TextEditingController();
TextEditingController assetGroupDescriptionController = new TextEditingController();
String assetGroupTypeController;
Widget build(BuildContext context) {
ProgressDialog pr;
assetGroupNameController.text = widget.assetGroup.name;
assetGroupDescriptionController.text = widget.assetGroup.description;
assetGroupTypeController = widget.assetGroup.type;
return ListView(
children: <Widget>[
Card(
elevation: 13.0,
child: Form(
key: _formKey,
child: DropdownButtonFormField(
value: assetGroupTypeController,
items: assetGroupTypes.map((f) {
return new DropdownMenuItem<String>(
value: f['key'],
child: new Text(f['text']),
);
}).toList(),
onChanged: (value) {
typeDropdownChange(value);
})
)
)
);
}
void typeDropdownChange(value) {
setState(() {
assetGroupTypeController = value;
});
}
You assigned the controller directly to value parameter of DropdownButtonFormField and you have string value for DropdownMenuItem. You should be storing the same data type value. Check below example and modify your code accordingly
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Material(
child: Center(
child: new MyDropDown(),
),
),
);
}
}
class MyDropDown extends StatefulWidget {
const MyDropDown({
Key key,
}) : super(key: key);
#override
_MyDropDownState createState() => _MyDropDownState();
}
class _MyDropDownState extends State<MyDropDown> {
String selected;
#override
Widget build(BuildContext context) {
return DropdownButtonFormField<String>(
value: selected,
items: ["Item 1", "Item 2", "Item 3"]
.map((label) => DropdownMenuItem<String>(
child: Text(label),
value: label,
))
.toList(),
onChanged: (value) {
setState(() => selected = value);
},
);
}
}

How to assign i unique ID or key to SwitchListTile and retrieve/get its value in onChanged in flutter mobile app

I am building 9 SwitchListTile using for loop, as now the button contains same code so am having trouble
in its onChanged as my each button will have specific event to perform, how should i achieve it? Is it possible to send the button text/id or anything unique based on which i can perform the specific tasks?
Here _onChanged(value, counter); 'counter' is nothing but you can assume a variable in for loop assigning values 1-9 for each button. So Onchange i should know which button was pressed!.
I tried assigning // key: ValueKey(counter), to SwitchListTile constructor but was unable to retrieve that value in onChanged.
class MySwitchListTilesContainer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[800],
body: ListView(
children: List.generate(20, (i)=>MySwitchListTile(
)),
),
);
}
}
class MySwitchListTile extends StatefulWidget {
#override
_MySwitchListTileState createState() => new _MySwitchListTileState();
}
class _MySwitchListTileState extends State<MySwitchListTile> {
bool _v = false;
#override
Widget build(BuildContext context) {
return SwitchListTile(
value:_v,
onChanged: (value) {
_onChanged(value, counter);
},
);
}
}
void _onChanged(bool _v, int index) {
setState(() {
_v = _v;
if (index == 1) {
print(index);
} else {
print(index +1);
}
});
}
You can copy paste run full code below
You can pass callback to use in onChanged
code snippet
ListView(
children: List.generate(
20,
(i) => MySwitchListTile(
v: false,
callback: () {
print("index is $i");
setState(() {
});
},
)),
)
...
class MySwitchListTile extends StatefulWidget {
final bool v;
final VoidCallback callback;
...
return SwitchListTile(
value: widget.v,
onChanged: (value) {
widget.callback();
},
);
working demo
output of working demo
I/flutter ( 6597): index is 0
I/flutter ( 6597): index is 2
I/flutter ( 6597): index is 6
full code
import 'package:flutter/material.dart';
class MySwitchListTilesContainer extends StatefulWidget {
#override
_MySwitchListTilesContainerState createState() => _MySwitchListTilesContainerState();
}
class _MySwitchListTilesContainerState extends State<MySwitchListTilesContainer> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[800],
body: ListView(
children: List.generate(
20,
(i) => MySwitchListTile(
v: false,
callback: () {
print("index is $i");
setState(() {
});
},
)),
),
);
}
}
class MySwitchListTile extends StatefulWidget {
final bool v;
final VoidCallback callback;
const MySwitchListTile({Key key, this.v, this.callback}) : super(key: key);
#override
_MySwitchListTileState createState() => new _MySwitchListTileState();
}
class _MySwitchListTileState extends State<MySwitchListTile> {
#override
Widget build(BuildContext context) {
return SwitchListTile(
value: widget.v,
onChanged: (value) {
widget.callback();
},
);
}
}
/*void _onChanged(bool _v, int index) {
setState(() {
_v = _v;
if (index == 1) {
print(index);
} else {
print(index + 1);
}
});
}*/
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MySwitchListTilesContainer(),
);
}
}

How to display video from path_provider in flutter?

FLutter:
How to display video in video_player from the location of path_provider ?
you can copy paste run full code below
In demo, I use getApplicationDocumentsDirectory. you can print full path to check
make sure you have a file located in
/data/user/0/your_proejct_name/app_flutter/Movies/2019-11-08.mp4
code snippet
Future<String> load_path_video() async {
loading = true;
final Directory extDir = await getApplicationDocumentsDirectory();
setState(() {
dirPath = '${extDir.path}/Movies/2019-11-08.mp4';
print(dirPath);
loading = false;
// if I print ($dirPath) I have /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4
});
}
Container(
padding: const EdgeInsets.all(20),
child: loading
? CircularProgressIndicator()
: NetworkPlayerLifeCycle(
'$dirPath', // with the String dirPath I have error but if I use the same path but write like this /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4 it's ok ... why ?
(BuildContext context, VideoPlayerController controller) =>
AspectRatioVideo(controller)),
),
working demo
full code
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:video_player/video_player.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String dirPath;
bool loading = false;
Future<String> load_path_video() async {
loading = true;
final Directory extDir = await getApplicationDocumentsDirectory();
setState(() {
dirPath = '${extDir.path}/Movies/2019-11-08.mp4';
print(dirPath);
loading = false;
// if I print ($dirPath) I have /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4
});
}
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
void initState() {
// TODO: implement initState
load_path_video();
super.initState();
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
body: ListView(
children: <Widget>[
Container(
padding: const EdgeInsets.all(20),
child: loading
? CircularProgressIndicator()
: NetworkPlayerLifeCycle(
'$dirPath', // with the String dirPath I have error but if I use the same path but write like this /data/user/0/com.XXXXX.flutter_video_test/app_flutter/Movies/2019-11-08.mp4 it's ok ... why ?
(BuildContext context, VideoPlayerController controller) =>
AspectRatioVideo(controller)),
),
],
),
);
}
}
class VideoPlayPause extends StatefulWidget {
VideoPlayPause(this.controller);
final VideoPlayerController controller;
#override
State createState() {
return _VideoPlayPauseState();
}
}
class _VideoPlayPauseState extends State<VideoPlayPause> {
_VideoPlayPauseState() {
listener = () {
setState(() {});
};
}
FadeAnimation imageFadeAnim =
FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0));
VoidCallback listener;
VideoPlayerController get controller => widget.controller;
#override
void initState() {
super.initState();
controller.addListener(listener);
controller.setVolume(1.0);
controller.play();
}
#override
void deactivate() {
controller.setVolume(0.0);
controller.removeListener(listener);
super.deactivate();
}
#override
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[
GestureDetector(
child: VideoPlayer(controller),
onTap: () {
if (!controller.value.initialized) {
return;
}
if (controller.value.isPlaying) {
imageFadeAnim =
FadeAnimation(child: const Icon(Icons.pause, size: 100.0));
controller.pause();
} else {
imageFadeAnim =
FadeAnimation(child: const Icon(Icons.play_arrow, size: 100.0));
controller.play();
}
},
),
Align(
alignment: Alignment.bottomCenter,
child: VideoProgressIndicator(
controller,
allowScrubbing: true,
),
),
Center(child: imageFadeAnim),
Center(
child: controller.value.isBuffering
? const CircularProgressIndicator()
: null),
];
return Stack(
fit: StackFit.passthrough,
children: children,
);
}
}
class FadeAnimation extends StatefulWidget {
FadeAnimation(
{this.child, this.duration = const Duration(milliseconds: 500)});
final Widget child;
final Duration duration;
#override
_FadeAnimationState createState() => _FadeAnimationState();
}
class _FadeAnimationState extends State<FadeAnimation>
with SingleTickerProviderStateMixin {
AnimationController animationController;
#override
void initState() {
super.initState();
animationController =
AnimationController(duration: widget.duration, vsync: this);
animationController.addListener(() {
if (mounted) {
setState(() {});
}
});
animationController.forward(from: 0.0);
}
#override
void deactivate() {
animationController.stop();
super.deactivate();
}
#override
void didUpdateWidget(FadeAnimation oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.child != widget.child) {
animationController.forward(from: 0.0);
}
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return animationController.isAnimating
? Opacity(
opacity: 1.0 - animationController.value,
child: widget.child,
)
: Container();
}
}
typedef Widget VideoWidgetBuilder(
BuildContext context, VideoPlayerController controller);
abstract class PlayerLifeCycle extends StatefulWidget {
PlayerLifeCycle(this.dataSource, this.childBuilder);
final VideoWidgetBuilder childBuilder;
final String dataSource;
}
/// A widget connecting its life cycle to a [VideoPlayerController] using
/// a data source from the network.
class NetworkPlayerLifeCycle extends PlayerLifeCycle {
NetworkPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder)
: super(dataSource, childBuilder);
#override
_NetworkPlayerLifeCycleState createState() => _NetworkPlayerLifeCycleState();
}
/// A widget connecting its life cycle to a [VideoPlayerController] using
/// an asset as data source
class AssetPlayerLifeCycle extends PlayerLifeCycle {
AssetPlayerLifeCycle(String dataSource, VideoWidgetBuilder childBuilder)
: super(dataSource, childBuilder);
#override
_AssetPlayerLifeCycleState createState() => _AssetPlayerLifeCycleState();
}
abstract class _PlayerLifeCycleState extends State<PlayerLifeCycle> {
VideoPlayerController controller;
#override
/// Subclasses should implement [createVideoPlayerController], which is used
/// by this method.
void initState() {
super.initState();
controller = createVideoPlayerController();
controller.addListener(() {
if (controller.value.hasError) {
print(controller.value.errorDescription);
}
});
controller.initialize();
controller.setLooping(true);
controller.play();
}
#override
void deactivate() {
super.deactivate();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.childBuilder(context, controller);
}
VideoPlayerController createVideoPlayerController();
}
class _NetworkPlayerLifeCycleState extends _PlayerLifeCycleState {
#override
VideoPlayerController createVideoPlayerController() {
return VideoPlayerController.network(widget.dataSource);
}
}
class _AssetPlayerLifeCycleState extends _PlayerLifeCycleState {
#override
VideoPlayerController createVideoPlayerController() {
return VideoPlayerController.asset(widget.dataSource);
}
}
/// A filler card to show the video in a list of scrolling contents.
Widget buildCard(String title) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: const Icon(Icons.airline_seat_flat_angled),
title: Text(title),
),
// TODO(jackson): Remove when deprecation is on stable branch
// ignore: deprecated_member_use
ButtonTheme.bar(
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('BUY TICKETS'),
onPressed: () {
/* ... */
},
),
FlatButton(
child: const Text('SELL TICKETS'),
onPressed: () {
/* ... */
},
),
],
),
),
],
),
);
}
class VideoInListOfCards extends StatelessWidget {
VideoInListOfCards(this.controller);
final VideoPlayerController controller;
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
buildCard("Item a"),
buildCard("Item b"),
buildCard("Item c"),
buildCard("Item d"),
buildCard("Item e"),
buildCard("Item f"),
buildCard("Item g"),
Card(
child: Column(children: <Widget>[
Column(
children: <Widget>[
const ListTile(
leading: Icon(Icons.cake),
title: Text("Video video"),
),
Stack(
alignment: FractionalOffset.bottomRight +
const FractionalOffset(-0.1, -0.1),
children: <Widget>[
AspectRatioVideo(controller),
Image.asset('assets/flutter-mark-square-64.png'),
]),
],
),
])),
buildCard("Item h"),
buildCard("Item i"),
buildCard("Item j"),
buildCard("Item k"),
buildCard("Item l"),
],
);
}
}
class AspectRatioVideo extends StatefulWidget {
AspectRatioVideo(this.controller);
final VideoPlayerController controller;
#override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController get controller => widget.controller;
bool initialized = false;
VoidCallback listener;
#override
void initState() {
super.initState();
listener = () {
if (!mounted) {
return;
}
if (initialized != controller.value.initialized) {
initialized = controller.value.initialized;
setState(() {});
}
};
controller.addListener(listener);
}
#override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: VideoPlayPause(controller),
),
);
} else {
return Container();
}
}
}