Update a variable after executing a future function - flutter

I have a function called ff which takes 2 images, and returns an outputimage , I"d like to update the original image, when the user taps on any of the style images, and display the outputimage instead of the original one . I know I should use setstate somehow, but I'm confused on how and where
code :
Future ff(String styleImagePath, String originalImagePath) async {
ImageTransferFacade showtime = ImageTransferFacade();
var original_image = showtime.loadoriginalImage(originalImagePath);
var style_image = showtime.loadStyleImage(styleImagePath);
var output_image = showtime.transfer(await original_image, await style_image);
return output_image;
}
class second extends StatefulWidget {
const second({
Key? key,
required this.image,
}) : super(key: key);
final XFile image;
#override
State<second> createState() => _secondState();
}
class _secondState extends State<second> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(16),
child: Card(
child: Image.file(File(widget.image
.path))))), // this is the original image which i'd like to replace once user taps on the style image
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: CarouselSlider.builder(
itemCount: imageList.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: GestureDetector(
onTap: () => ff(imageList[index], widget.image.path),
child: Image.network(imageList[index],
fit: BoxFit.cover, width: 1000)),
));
},
),
),
floatingActionButton: const FloatingActionButton(onPressed: null),
);
}
}

Try this:
Future ff(String styleImagePath, String originalImagePath) async {
ImageTransferFacade showtime = ImageTransferFacade();
var original_image = showtime.loadoriginalImage(originalImagePath);
var style_image = showtime.loadStyleImage(styleImagePath);
var output_image = showtime.transfer(await original_image, await style_image);
return output_image;
}
class second extends StatefulWidget {
const second({
Key? key,
required this.image,
}) : super(key: key);
final XFile image;
#override
State<second> createState() => _secondState();
}
class _secondState extends State<second> {
late XFile _image;
initState(){
_image = widget.image;
super.initState();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(16),
child: Card(
child: Image.file(File(image
.path))))), // this is the original image which i'd like to replace once user taps on the style image
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: CarouselSlider.builder(
itemCount: imageList.length,
options: CarouselOptions(
autoPlay: true,
aspectRatio: 2.0,
enlargeCenterPage: true,
),
itemBuilder: (context, index, realIdx) {
return Container(
child: Center(
child: GestureDetector(
onTap: () {
ff(imageList[index], widget.image.path).then((value){
setState((){
_image = value;
});
});
}, child: Image.network(imageList[index],
fit: BoxFit.cover, width: 1000)),
));
},
),
),
floatingActionButton: const FloatingActionButton(onPressed: null),
);
}
}

Related

Random sources for infinite scrolling GridView items

I want to have an infinite scrolling GridView page in which items have different sources, which is defined by randomly_select_URL function in my code. I need each item to have a different random_select_URL and selectedImage value while after running my code, all of the items are the same. Could anyone help with this?
The main page code
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
import 'package:pet_store/widgets/random_pet_image.dart';
import 'webservice/API.dart';
import 'main.dart';
class Infinite_Scroll_Game extends StatefulWidget {
const Infinite_Scroll_Game({Key? key}) : super(key: key);
#override
State<Infinite_Scroll_Game> createState() => _Infinite_Scroll_GameState();
}
class _Infinite_Scroll_GameState extends State<Infinite_Scroll_Game> {
ScrollController _scrollController = ScrollController();
int pageNumber = 1;
var myRecipe;
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
pageNumber++;
setState(() {});
}
});
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.indigo,
title: const Text('Infinite Scroll Game'),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () {
// Navigator.pop(context);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const HomePage(),
),
(route) => false,
);
},
),
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 12.0),
child: FutureBuilder<List<dynamic>>(
future: API.get_pets(randomly_select_URL()),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
return GridView.builder(
controller: _scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12.0,
mainAxisSpacing: 12.0,
),
itemBuilder: (BuildContext context, int index) {
return Random_Image_Card(
pet_data: pet_data, random_pet: random_pet);
},
);
} else if (snapshot.hasError) {
return Center(
child: Text('There was an error, Please try again'),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
The item code:
import 'package:double_back_to_close/toast.dart';
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
class Random_Image_Card extends StatefulWidget {
List<dynamic>? pet_data;
int random_pet;
int current_index = 0;
Random_Image_Card(
{this.pet_data, required this.random_pet, Key? key})
: super(key: key);
#override
State<Random_Image_Card> createState() => _Random_Image_CardState();
}
class _Random_Image_CardState extends State<Random_Image_Card> {
List<dynamic> photoURL = [];
var number_of_photos;
var selectedImage;
#override
void initState() {
photoURL = widget.pet_data![widget.random_pet].photoUrls;
number_of_photos = photoURL.length;
selectedImage = random.nextInt(number_of_photos);
}
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 180,
width: 180,
child: Card(
child: Container(
decoration: (photoURL.length != 0)
? BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: image(photoURL[selectedImage]).image,
fit: BoxFit.scaleDown),
)
: const BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: NetworkImage(
"https://cdn-cziplee-estore.azureedge.net//cache/no_image_uploaded-253x190.png"),
fit: BoxFit.scaleDown),
),
child: Text(""),
),
),
),
],
);
}
}
I added FutureBuilder to the item and just added the item in the main page and it fixed,
import 'package:double_back_to_close/toast.dart';
import 'package:flutter/material.dart';
import 'package:pet_store/utils/utils.dart';
import '../webservice/API.dart';
class Random_Image_Card extends StatefulWidget {
const Random_Image_Card({Key? key}) : super(key: key);
#override
State<Random_Image_Card> createState() => _Random_Image_CardState();
}
class _Random_Image_CardState extends State<Random_Image_Card> {
List<dynamic>? pet_data;
int random_pet = 0;
int current_index = 0;
List<dynamic> photoURL = [];
var number_of_photos;
var selectedImage;
var random_URL;
#override
Widget build(BuildContext context) {
random_URL = randomly_select_URL();
return FutureBuilder<List<dynamic>>(
future: API.get_pets(random_URL),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
photoURL = pet_data![random_pet].photoUrls;
number_of_photos = photoURL.length;
selectedImage = random.nextInt(number_of_photos);
return Column(
children: [
SizedBox(
height: 180,
width: 180,
child: Card(
child: Container(
decoration: (photoURL.length != 0)
? BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: image(photoURL[selectedImage]).image,
fit: BoxFit.scaleDown),
)
: const BoxDecoration(
image: DecorationImage(
alignment: Alignment.center,
image: NetworkImage(
"https://cdn-cziplee-estore.azureedge.net//cache/no_image_uploaded-253x190.png"),
fit: BoxFit.scaleDown),
),
child: Text(""),
),
),
),
],
);
} else if (snapshot.hasError) {
return const Center(
child: Text('There was an error, Please try again'),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}

Updating a widget from parameters passed from another widget to re-render the UI

I have two widgets one is a listview builder that displays a list of buttons horizontally from a rest API and the other a future builder that displays images from another rest API based on the category name of the button.
Pressing a category button does pass the value to the fetchPhotos future I have created and I can see the category name appear in the debug console. However, the UI never re-renders, despite changing the variable value and calling fetchPhoto inside setState onPressed on a category button.
I've made a comment in the categoryList below where I thought I had it solved but to no avail
// SET STATE TO UPDATE fetchPhotos AND RE-RENDER UI NOT WORKING
How can I go about getting the ui to refresh with the newly fetched JSON data from the fetchPhotos future with the updated querystring parameter added?
Future<List<Category>> fetchCategories(http.Client client) async {
final response = await client
.get(Uri.parse('./categories'));
return compute(parseCategories, response.body);
}
List<Category> parseCategories(String responseBody) {
final parsed1 = jsonDecode(responseBody);
parsed1.insert(0, {"id": "0", "name": "All");
final parsed = parsed1.cast<Map<String, dynamic>>();
return parsed.map<Category>((json) => Category.fromJson(json)).toList();
}
class Category {
final String id;
final String name;
final int useCount;
const Category(
{required this.id, required this.name, required this.useCount});
factory Category.fromJson(Map<String, dynamic> json) {
return Category(
id: json['id'] as String,
name: json['name'] as String
);
}
}
class CategoryList extends StatefulWidget {
const CategoryList({Key? key, required this.categories}) : super(key: key);
final List<Category> categories;
#override
State<CategoryList> createState() => _CategoryListState();
}
var chosenCategory = "all";
class _CategoryListState extends State<CategoryList> {
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.categories.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(left: 2, right: 2),
child: ElevatedButton(
onPressed: () {
setState(() {
// SET STATE TO UPDATE fetchPhotos AND RE-RENDER UI NOT WORKING
chosenCategory = widget.categories[index].name;
fetchPhotos(http.Client(), chosenCategory);
});
},
child: Text(widget.categories[index].name),
));
},
);
}
}
Future<List<Photo>> fetchPhotos(http.Client client, chosenCategory) async {
print("---- CHOSEN CATEGORY ------ $chosenCategory");
print(chosenCategory);
final response = await client.get(Uri.parse(
' 'https://localhost/photoapp/getPhotos?searchString=$chosenCategory'));
'));
return compute(parsePhotos, response.body);
}
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final String id;
final String name;
final String imageUrl;
const Photo({
required this.id,
required this.name,
required this.imageUrl,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as String,
name: json['name'] as String,
imageUrl: json['imageUrl'] as String,
);
}
}
class PhotosList extends StatefulWidget {
const PhotosList({Key? key, required this.photos}) : super(key: key);
final List<Photo> photos;
#override
State<PhotosList> createState() => _PhotosListState();
}
class _PhotosListState extends State<PhotosList> {
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: widget.photos.length,
itemBuilder: (context, index) {
return Stack(
alignment: Alignment.topCenter,
children: <Widget>[
InkWell(
onTap: () { },
child: Container(
height: 300.0,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
alignment: FractionalOffset.center,
image: widget.photos[index].imageUrl,
),
),
),
)), // Container Image
Container(
margin: const EdgeInsets.only(top: 150),
width: MediaQuery.of(context).size.width * 0.92,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(widget.photos[index].name,
style: TextStyle(fontSize: 12, color: Colors.white),
maxLines: 1,
textAlign: TextAlign.center)),
decoration: BoxDecoration(
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.8),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(1, 1),
),
],
),
),
],
);
},
);
}
}
class AlbumPage extends StatefulWidget {
AlbumPage({Key, key, this.title}) : super(key: key);
final String? title;
#override
_AlbumState createState() => _ClbumState();
}
class _AlbumState extends State<AlbumPage> {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AlbumCubit(AlbumState.initial),
child: AlbumWidget(),
);
}
}
class AlbumWidget extends StatefulWidget {
#override
_AlbumWidgetState createState() => _AlbumWidgetState();
}
class _AlbumWidgetState extends State<AlbumWidget> {
#override
void initState() {
super.initState();
}
appBar() {
return AppBar(
title: const Text("Photo Album"),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
));
}
getCategories() {
return Container(
padding: const EdgeInsets.all(14.0),
color: Colors.grey,
width: double.infinity,
height: 60,
child: FutureBuilder<List<Category>>(
future: fetchCategories(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return CategoryList(categories: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
getPhotos() {
return Flexible(
child: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client(), chosenCategory),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return PhotosList(photos: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar(),
body: Column(
children: <Widget>[
getCategories(),
getPhotos(),
],
));
}
}

How can I properly remove an OverlayEntry in flutter?

In my main widget tree, I have a GestureDetector that when tapped, will launch an Overlay as follows:
OverlayState? _overlayState = Overlay.of(context);
_overlayState?.insert(
OverlayEntry(
builder: (BuildContext context) {
return ShowNotificationIcon();
},
)
);
SnowNotificationIcon is actually a StatefulWidget that houses the guts of the Overlay:
class ShowNotificationIcon extends ConsumerStatefulWidget {
const ShowNotificationIcon({Key? key}) : super(key: key);
#override
_ShowNotificationIconState createState() => _ShowNotificationIconState();
}
class _ShowNotificationIconState extends ConsumerState<ShowNotificationIcon> {
void initState(){
super.initState();
}
void dispose(){
super.dispose();
}
Positioned theDropDown(){
return
Positioned(
top: 50.0,
left: 50.0,
child: Material(
color: Colors.transparent,
child:
Column(children: [
Text('Test!'),
],)),
);
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GestureDetector(
onTap: () {
/// I WANT TO REMOVE THE OVERLAY HERE
},
child: Container(
color: Colors.transparent,
),
)
),
theDropDown()
],
);
}
}
As I understand it, the overlay must be removed via a .remove() call, but since the overlay is all housed within a StatefulWidget, how can I make a .remove call on the overlay when it was opened outside of the StateWidget?
Am I missing something obvious here?
You can try this example I created for you
OverlayState? _overlayState = Overlay.of(context);
OverlayEntry? _overlayEntry;
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return ShowNotificationIcon(entry: _overlayEntry);
},
);
_overlayState?.insert(_overlayEntry);
class ShowNotificationIcon extends ConsumerStatefulWidget {
final OverlayEntry? entry;
const ShowNotificationIcon({Key? key, this.entry}) : super(key: key);
#override
_ShowNotificationIconState createState() => _ShowNotificationIconState();
}
class _ShowNotificationIconState extends ConsumerState<ShowNotificationIcon> {
Positioned theDropDown(){
return
Positioned(
top: 50.0,
left: 50.0,
child: Material(
color: Colors.transparent,
child:
Column(children: [
Text('Test!'),
],)),
);
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GestureDetector(
onTap: () {
if (widget.entry != null){
widget.entry.remove();
}
},
child: Container(
color: Colors.transparent,
),
)
),
theDropDown()
],
);
}
}
Here, A sample Toast class. I use close button in overlay. You can use similarly.
import 'package:flutter/material.dart';
class AppDialogs {
static final AppDialogs _instance = AppDialogs.internal();
AppDialogs.internal();
factory AppDialogs() => _instance;
static void appToast(
BuildContext context, {
required Widget title,
Widget? description,
Icon? toastIcon,
Color? toastColor,
double? height,
double? width,
bool dismissibleToast = true,
}) async {
final OverlayState? overlayState = Overlay.of(context);
late OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(
builder: (content) => Positioned(
height: height ?? 80,
width: width ?? 200,
top: 0,
right: 0,
child: Card(
borderOnForeground: true,
elevation: 10,
child: Stack(
children: [
ListTile(
tileColor: toastColor,
title: title,
subtitle: description,
leading: toastIcon,
),
Positioned(
top: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () => closeOverlay(overlayEntry),
child: const Icon(
Icons.close,
size: 14,
),
),
),
),
],
))),
);
overlayState!.insert(overlayEntry);
}
static void closeOverlay(OverlayEntry overlayEntry) {
{
overlayEntry.remove();
}
}
}
class _LoginViewState extends State<LoginView> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TextButton(
child: Text("Login"),
onPressed: (() {
AppDialogs.appToast(context, title: Text("Toast"));
}),
)
],
),
);
}

How to create load more listview in flutter

I want to create load more scrollview in listview. My app flow is storing youtube link in csv file and fetch this link from my app and display in my listview. But the problem is I don't want to wait too much load time when app is open.If I have a lot of youtube link in my csv.I will take a lot of time.So,for example I want to display only 5 video in initial state and when load more, display more 5 video in my list view.How can I do that.My code is below.
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'videolist.dart';
import './models/models.dart';
import 'package:csv/csv.dart' as csv;
import 'package:http/http.dart' as http;
class DisplayVideo extends StatefulWidget {
String id;
#override
DisplayVideo(this.id);
_DisplayVideoState createState() => _DisplayVideoState();
}
class _DisplayVideoState extends State<DisplayVideo> {
late YoutubePlayerController _controller ;
Future<List<YoutubeDetail>> _loadCSV() async {
Map<String, String> allData = {
'login': '',
'password': '',
};
final Uri url = Uri.parse(
'https://raw.githubusercontent.com/JornaldRem/bedtime_story/main/videoId.csv');
final response = await http.get(url);
csv.CsvToListConverter converter =
new csv.CsvToListConverter(eol: '\r\n', fieldDelimiter: ',');
List<List> listCreated = converter.convert(response.body);
// the csv file is converted to a 2-Dimensional list
List<YoutubeDetail> youtubeDetailList = [];
for (int i = 0; i < listCreated.length; i++) {
YoutubeDetail temp = YoutubeDetail(
listCreated[i][0],
listCreated[i][1],
);
youtubeDetailList.add(temp);
}
return youtubeDetailList;
}
#override
void initState() {
// TODO: implement initState
super.initState();
_controller = YoutubePlayerController(
initialVideoId: widget.id,
flags: YoutubePlayerFlags(
autoPlay: true,
mute: false,
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: Text('Title'),
toolbarHeight: 60,
backgroundColor: const Color(0xFF006666),
),
body: Column(
children: [
Container(
child: YoutubePlayer(
controller: _controller,
liveUIColor: Colors.amber,
),
),
Expanded(
child: Container(
child: FutureBuilder(
future: _loadCSV(),
builder: (BuildContext context,
AsyncSnapshot<List<YoutubeDetail>> snapshot) {
if (snapshot.hasData) {
List<YoutubeDetail> videoDetail = snapshot.data!;
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: videoDetail.length,
itemBuilder: (_, int index) {
if (index > 0) {
return GestureDetector(
child: Container(
height: 80,
child: DisplayVideoView(
videoDetail[index].url,
videoDetail[index].title),
),
onTap: (){
String url = videoDetail[index].url;
String id = url.substring(url.length - 11);
print("HEllo");
_controller.load(id);
// DisplayVideo(id);
}
);
} else {
return Container();
}
});
} else {
return Container();
}
}),
),
),
],
));
}
}
class DisplayVideoView extends StatelessWidget {
String videopath;
String title;
DisplayVideoView(this.videopath, this.title);
#override
Widget build(BuildContext context) {
String url = videopath;
String id = url.substring(url.length - 11);
// TODO: implement build
return Card(
clipBehavior: Clip.antiAlias,
child: Container(
height: 150,
padding: const EdgeInsets.all(0),
child: Row(children: [
Expanded(
flex: 6,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://img.youtube.com/vi/$id/mqdefault.jpg'),
fit: BoxFit.fill)),
),
),
Spacer(
flex: 1,
),
Expanded(
flex: 14,
child: Container(
padding: const EdgeInsets.only(top: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold)),
],
),
),
),
]),
),
);
}
}
What do you think about this approach:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<Widget> _myList = [];
void _loadFiveMore() {
_myList = <Widget>[
..._myList,
for (int i = _myList.length; i < _myList.length + 5; i++)
ListTile(title: Text('item $i')),
];
}
#override
void initState() {
_loadFiveMore();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(children: [
..._myList,
OutlinedButton(
onPressed: () {
setState(() => _loadFiveMore());
},
child: const Text('get 5 more'))
]),
),
);
}
}
void main() {
runApp(const ExampleWidget());
}
You can use this package.
have loadmore callback, refresh call back
https://pub.dev/packages/loadmore_listview

How to Navigate in Same the Page Master Detail page Flutter

I am building a master detail based app and I want to show in splitview. Trying to understand how to push data to another page in same view but couldn't. Want to cover details data in second page. How to push data?
It could be either responsive or not. But I don't want to resolve but only using set state and fill the blank in details page
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: VerticalSplitView(
left: ListView.builder( itemCount: 12,
itemBuilder: (context, index) {
return Card(
child: InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
//Navigator.push(context, MaterialPageRoute(builder: (context) => new yapiekle()) );
},
child: Container(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
//Center Column contents vertically,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListTile(
leading: Image.network("https://picsum.photos/200/300"),
title: Text("Title"),
subtitle: Text("Subtitle")),
),
//Spacer(),
],
),
),
),
),
);
}
),
right: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Center(
child: FlutterLogo(
size: 256,
)),
),
),
),
);
}
}
class VerticalSplitView extends StatefulWidget {
final Widget left;
final Widget right;
final double ratio;
const VerticalSplitView(
{Key key, #required this.left, #required this.right, this.ratio = 0.5})
: assert(left != null),
assert(right != null),
assert(ratio >= 0),
assert(ratio <= 1),
super(key: key);
#override
_VerticalSplitViewState createState() => _VerticalSplitViewState();
}
class _VerticalSplitViewState extends State<VerticalSplitView> {
final _dividerWidth = 16.0;
//from 0-1
double _ratio;
double _maxWidth;
get _width1 => _ratio * _maxWidth;
get _width2 => (1 - _ratio) * _maxWidth;
#override
void initState() {
super.initState();
_ratio = widget.ratio;
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(_ratio <= 1);
assert(_ratio >= 0);
if (_maxWidth == null) _maxWidth = constraints.maxWidth - _dividerWidth;
if (_maxWidth != constraints.maxWidth) {
_maxWidth = constraints.maxWidth - _dividerWidth;
}
return SizedBox(
width: constraints.maxWidth,
child: Row(
children: <Widget>[
SizedBox(
width: _width1,
child: widget.left,
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: _dividerWidth,
height: constraints.maxHeight,
child: RotationTransition(
child: Icon(Icons.drag_handle),
turns: AlwaysStoppedAnimation(0.25),
),
),
onPanUpdate: (DragUpdateDetails details) {
setState(() {
_ratio += details.delta.dx / _maxWidth;
if (_ratio > 1)
_ratio = 1;
else if (_ratio < 0.0) _ratio = 0.0;
});
},
),
SizedBox(
width: _width2,
child: widget.right,
),
],
),
);
});
}
}
I assumed that you want to change the the right page by clicking on the left card widgets. I have been developed something like this. I am using IndexedStack for render on the right side of VerticalSplitView then use provider and consumer for controlling the page to display.
First of all you need to import provider dependency in pubspec.ymal
You can replace this code below for entire of main.dart.
In main.dart you can try to replace this code. The idea is we are going to create IndexedStack that contain the Widget (Page as you prefer). Then we are going to change the index of IndexedStack by using Provider and Consumer.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_indexed_stack/page_data.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) {
var pageData = PageData();
return pageData;
}),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// Set required page same as list length in left of VerticalSplitView
List<Widget> pages = [Text('Page1'), Text('Page2'), Text('Page3'),
Text('Page4'), Text('Page5'), Text('Page6'), Text('Page7'),
Text('Page8'), Text('Page9'), Text('Page10'), Text('Page11'),
Text('Page12'), ];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: VerticalSplitView(
left: ListView.builder( itemCount: 12,
itemBuilder: (context, index) {
return Card(
child: InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
// Set the current page for change page on the right side.
Provider.of<PageData>(context, listen: false).setCurrentTab(index);
},
child: Container(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
//Center Column contents vertically,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListTile(
leading: Image.network("https://picsum.photos/200/300"),
title: Text("Title"),
subtitle: Text("Subtitle")),
),
//Spacer(),
],
),
),
),
),
);
}
),
right: Consumer<PageData>(
builder: (context, pageData, child) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: IndexedStack(
children: pages,
index: pageData.currentPage,
)
);
},
),
),
),
);
}
}
class VerticalSplitView extends StatefulWidget {
final Widget left;
final Widget right;
final double ratio;
const VerticalSplitView(
{Key key, #required this.left, #required this.right, this.ratio = 0.5})
: assert(left != null),
assert(right != null),
assert(ratio >= 0),
assert(ratio <= 1),
super(key: key);
#override
_VerticalSplitViewState createState() => _VerticalSplitViewState();
}
class _VerticalSplitViewState extends State<VerticalSplitView> {
final _dividerWidth = 16.0;
//from 0-1
double _ratio;
double _maxWidth;
get _width1 => _ratio * _maxWidth;
get _width2 => (1 - _ratio) * _maxWidth;
#override
void initState() {
super.initState();
_ratio = widget.ratio;
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(_ratio <= 1);
assert(_ratio >= 0);
if (_maxWidth == null) _maxWidth = constraints.maxWidth - _dividerWidth;
if (_maxWidth != constraints.maxWidth) {
_maxWidth = constraints.maxWidth - _dividerWidth;
}
return SizedBox(
width: constraints.maxWidth,
child: Row(
children: <Widget>[
SizedBox(
width: _width1,
child: widget.left,
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: _dividerWidth,
height: constraints.maxHeight,
child: RotationTransition(
child: Icon(Icons.drag_handle),
turns: AlwaysStoppedAnimation(0.25),
),
),
onPanUpdate: (DragUpdateDetails details) {
setState(() {
_ratio += details.delta.dx / _maxWidth;
if (_ratio > 1)
_ratio = 1;
else if (_ratio < 0.0) _ratio = 0.0;
});
},
),
SizedBox(
width: _width2,
child: widget.right,
),
],
),
);
});
}
}
You need to create file for Provider and replce the code below.
import 'package:flutter/cupertino.dart';
class PageData extends ChangeNotifier{
PageData();
int _currentPage = 0;
void setCurrentTab(int index){
this._currentPage = index;
notifyListeners();
}
int get currentPage {
return this._currentPage;
}
}
Happy coding :)
if I now want to place a button inside the right widget: "add new post". This new-post-function should create a new post by copying all data from current page to the new post with a new post-ID. In the same function it then should navigate into the new post to edit copy of comment in a copy of current post. Like:
ElevatedButton(
onPressed: () {
addPost();
}
)
addPost() {
String newId = uuid.v1();
var newPost = Entry(
id: newId,
entry: entryProvider.comment,
);
firestoreService.setEntry(newPost);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(id: newId)));
}