I'm trying to make my 3D Perspective PageView to navigate to specific page when I click the center Image. I followed this tutorial on youtube (https://www.youtube.com/watch?v=o-98lLOxohw) and customized it on my own project.
This is how my project looks like right now when I run it.
And this is my home.dart file.
I'll include my github repository link if you want to have a better look.
https://github.com/loupdaniel/Second-Life_Mobile
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
Future<void> _launchURL(String url) async {
final Uri uri = Uri(scheme: "https", host: url);
if (!await launchUrl(
uri,
mode: LaunchMode.inAppWebView,
)) {
throw 'Can not launch url';
}
}
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
int index = 1;
int currentIndex = 0;
final PageController controller = PageController();
List<String> images = [
"https://i.ytimg.com/vi/PWADVtWyE9Q/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDcneFqOxHd28mCncQxT3jOErmk9Q",
"https://i.ytimg.com/vi/djzDWMy1z7k/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHwD_IA2ERzpZVxNvxCEOGr4fyTw",
"https://i.ytimg.com/vi/n8OxyKNBsuQ/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAtW45_cxRqEWfUVw19UMts_9Q0lQ",
"https://i.ytimg.com/vi/7bDFD_WcU9I/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAsgAH6VRN4w0HKtVc528WA5QSZ2w",
"https://i.ytimg.com/vi/_ABk7TmjnVk/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAxCeIml0HUbjJ3igi1FFe1esdwdg",
"https://i.ytimg.com/vi/-8m0XFea2zE/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDBOBRGDJeDjhT1HbRobSN2Tp6hMA",
"https://i.ytimg.com/vi/mXLS2IzZSdg/hq720.jpg?sqp=-oaymwE2CNAFEJQDSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhdIFsoZTAP&rs=AOn4CLDS13MjaIBxjjhccIktpAb0azBG9g",
"https://i.ytimg.com/vi/HuzlYAMwwJY/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmfMS9RENZuIJMQ8k2cf6MbHIpug",
"https://i.ytimg.com/vi/-nt_u4vo-DI/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAgUinltWhU-qqmgc_JroDLPt3OEg",
"https://i.ytimg.com/vi/tqtZIyN_Alg/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD4woxvyiNXgmSile7PLz7uoRPQOQ",
];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 223, 234, 244),
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('AppBar'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 15),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
},
),
),
),
),
),
Transform.translate(
offset: const Offset(0, -85),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Watch videos',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
//https://www.youtube.com/watch?v=7a_RXHOkJLM
//https://github.com/Programmer9211/Flutter-Carousel-Slider/blob/main/lib/main.dart
Transform.translate(
offset: const Offset(0, -65),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: SizedBox(
height: 162,
width: 335,
child: PageView.builder(
controller: controller,
onPageChanged: (index) {
setState(() {
currentIndex = index % images.length;
});
},
itemBuilder: (context, index) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 35),
child: SizedBox(
height: 100,
width: 400,
child: Image.network(
images[index % images.length],
fit: BoxFit.fill,
),
),
);
},
),
),
),
],
),
),
////Your Playlist of the week text
Transform.translate(
offset: const Offset(0, -30),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
Transform.translate(
offset: const Offset(0, -15),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album1.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album2.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album3.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album4.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album5.jpg',
height: 160.0,
width: 200.0,
),
),
),
],
),
),
],
),
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
You can use GestureDetector or InkWell Widget to handle onTap or touch function on any Widget. Add either of this Widget in your code
Sample Code : -
GestureDetector(
onTap: (() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondPage()), // Navigate to SecondPage
);
}),
child: Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
},
),
),
),
),
),
),
Full Code : -
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
Future<void> _launchURL(String url) async {
final Uri uri = Uri(scheme: "https", host: url);
if (!await launchUrl(
uri,
mode: LaunchMode.inAppWebView,
)) {
throw 'Can not launch url';
}
}
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
int index = 1;
int currentIndex = 0;
final PageController controller = PageController();
List<String> images = [
"https://i.ytimg.com/vi/PWADVtWyE9Q/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDcneFqOxHd28mCncQxT3jOErmk9Q",
"https://i.ytimg.com/vi/djzDWMy1z7k/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHwD_IA2ERzpZVxNvxCEOGr4fyTw",
"https://i.ytimg.com/vi/n8OxyKNBsuQ/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAtW45_cxRqEWfUVw19UMts_9Q0lQ",
"https://i.ytimg.com/vi/7bDFD_WcU9I/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAsgAH6VRN4w0HKtVc528WA5QSZ2w",
"https://i.ytimg.com/vi/_ABk7TmjnVk/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAxCeIml0HUbjJ3igi1FFe1esdwdg",
"https://i.ytimg.com/vi/-8m0XFea2zE/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDBOBRGDJeDjhT1HbRobSN2Tp6hMA",
"https://i.ytimg.com/vi/mXLS2IzZSdg/hq720.jpg?sqp=-oaymwE2CNAFEJQDSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhdIFsoZTAP&rs=AOn4CLDS13MjaIBxjjhccIktpAb0azBG9g",
"https://i.ytimg.com/vi/HuzlYAMwwJY/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmfMS9RENZuIJMQ8k2cf6MbHIpug",
"https://i.ytimg.com/vi/-nt_u4vo-DI/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAgUinltWhU-qqmgc_JroDLPt3OEg",
"https://i.ytimg.com/vi/tqtZIyN_Alg/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD4woxvyiNXgmSile7PLz7uoRPQOQ",
];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 223, 234, 244),
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('AppBar'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 15),
GestureDetector(
onTap: (() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SecondPage()), // Navigate to SecondPage
);
}),
child: Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
},
),
),
),
),
),
),
Transform.translate(
offset: const Offset(0, -85),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Watch videos',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
//https://www.youtube.com/watch?v=7a_RXHOkJLM
//https://github.com/Programmer9211/Flutter-Carousel-Slider/blob/main/lib/main.dart
Transform.translate(
offset: const Offset(0, -65),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: SizedBox(
height: 162,
width: 335,
child: PageView.builder(
controller: controller,
onPageChanged: (index) {
setState(() {
currentIndex = index % images.length;
});
},
itemBuilder: (context, index) {
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 35),
child: SizedBox(
height: 100,
width: 400,
child: Image.network(
images[index % images.length],
fit: BoxFit.fill,
),
),
);
},
),
),
),
],
),
),
////Your Playlist of the week text
Transform.translate(
offset: const Offset(0, -30),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
Transform.translate(
offset: const Offset(0, -15),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album1.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album2.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album3.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album4.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album5.jpg',
height: 160.0,
width: 200.0,
),
),
),
],
),
),
],
),
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
Related
I'm making this music app and when I click my image of the 3D Perspective PageView, the new page shows up well but the Curved Navigation Bar isn't showing up. I tired to find every resources on the internet but they are all saying about how to redirect with clicking the bottom nav bar.
This is my main.dart file
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/bottomnavbar.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Twitch Player Example',
//title: 'Perspective PageView',
theme: ThemeData(
useMaterial3: true,
),
home: const BottomNavBar(),
);
}
}
And this is my home.dart file.
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
import 'package:secondlife_mobile/screens/artist_1.dart';
import 'package:secondlife_mobile/screens/artist_2.dart';
import 'package:secondlife_mobile/screens/artist_3.dart';
import 'package:secondlife_mobile/screens/artist_4.dart';
import 'package:url_launcher/url_launcher.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
Future<void> _launchURL(String url) async {
final Uri uri = Uri(scheme: "https", host: url);
if (!await launchUrl(
uri,
mode: LaunchMode.inAppWebView,
)) {
throw 'Can not launch url';
}
}
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
int index = 1;
int currentIndex = 0;
final PageController controller = PageController();
List<String> images = [
"https://i.ytimg.com/vi/PWADVtWyE9Q/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDcneFqOxHd28mCncQxT3jOErmk9Q",
"https://i.ytimg.com/vi/djzDWMy1z7k/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHwD_IA2ERzpZVxNvxCEOGr4fyTw",
"https://i.ytimg.com/vi/n8OxyKNBsuQ/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAtW45_cxRqEWfUVw19UMts_9Q0lQ",
"https://i.ytimg.com/vi/7bDFD_WcU9I/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAsgAH6VRN4w0HKtVc528WA5QSZ2w",
"https://i.ytimg.com/vi/_ABk7TmjnVk/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAxCeIml0HUbjJ3igi1FFe1esdwdg",
"https://i.ytimg.com/vi/-8m0XFea2zE/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDBOBRGDJeDjhT1HbRobSN2Tp6hMA",
"https://i.ytimg.com/vi/mXLS2IzZSdg/hq720.jpg?sqp=-oaymwE2CNAFEJQDSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhdIFsoZTAP&rs=AOn4CLDS13MjaIBxjjhccIktpAb0azBG9g",
"https://i.ytimg.com/vi/HuzlYAMwwJY/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmfMS9RENZuIJMQ8k2cf6MbHIpug",
"https://i.ytimg.com/vi/-nt_u4vo-DI/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAgUinltWhU-qqmgc_JroDLPt3OEg",
"https://i.ytimg.com/vi/tqtZIyN_Alg/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD4woxvyiNXgmSile7PLz7uoRPQOQ",
];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 223, 234, 244),
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('AppBar'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 15),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
if (index == 0) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const FirstArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 1) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SecondArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 2) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ThirdArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 3) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const FourthArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
} else {
return const Text("Can't find anything");
}
},
),
),
),
),
),
Transform.translate(
offset: const Offset(0, -85),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Watch videos',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
//https://www.youtube.com/watch?v=7a_RXHOkJLM
//https://github.com/Programmer9211/Flutter-Carousel-Slider/blob/main/lib/main.dart
Transform.translate(
offset: const Offset(0, -65),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: SizedBox(
height: 162,
width: 335,
child: PageView.builder(
controller: controller,
onPageChanged: (index) {
setState(() {
currentIndex = index % images.length;
});
},
itemBuilder: (context, index) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
print("Tapped watch Videos Imageeeee");
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 35),
child: SizedBox(
height: 100,
width: 400,
child: Image.network(
images[index % images.length],
fit: BoxFit.fill,
),
),
),
);
},
),
),
),
],
),
),
////Your Playlist of the week text
Transform.translate(
offset: const Offset(0, -30),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
Transform.translate(
offset: const Offset(0, -15),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album1.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album2.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album3.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album4.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album5.jpg',
height: 160.0,
width: 200.0,
),
),
),
],
),
),
],
),
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
There is a package called persistent_bottom_nav_bar 5.0.2.
Step1: Create a new StatefulWidget called PersistentNavBar that contains the CurvedNavigationBar widget:
Step2: Wrap your entire app with a PageView and set its children to the pages you want to navigate to. For each page, you can set its bottomNavigationBar to the PersistentNavBar widget.
I'm trying to make a music app and I'm in trouble with modifying positions of 'Playlist of the week' text and the Listview of the images right now. I want to make those two properties more upwards but I have no idea what to do.
This is my home.dart file
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
int index = 1;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 223, 234, 244),
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('AppBar'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 35),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return MyPage(
number: index.toDouble(),
fraction: fraction,
);
}),
),
),
),
),
////Your Playlist of the week text
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
InkWell(
onTap: () {},
child: Ink(
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album1.jpg',
height: 160.0,
width: 200.0,
),
),
),
),
const SizedBox(
width: 30,
),
InkWell(
onTap: () {},
child: Ink(
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album2.jpg',
height: 160.0,
width: 200.0,
),
),
),
),
const SizedBox(
width: 30,
),
InkWell(
onTap: () {},
child: Ink(
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album3.jpg',
height: 160.0,
width: 200.0,
),
),
),
),
const SizedBox(
width: 30,
),
InkWell(
onTap: () {},
child: Ink(
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album4.jpg',
height: 160.0,
width: 200.0,
),
),
),
),
const SizedBox(
width: 30,
),
InkWell(
onTap: () {},
child: Ink(
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album5.jpg',
height: 160.0,
width: 200.0,
),
),
),
),
],
),
),
],
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
And this is the image that I'm expecting right now.
Yeah, I solved it with wrapping the Padding of both text and the ListView with Transform.translate and did some tinkering with the offset.
Thank God I solved this! :)
Try to place a column inside the singlechildscrollview and define mainaxisalignment as min
Set height property of Container widget wrapping PageView.
I am building an app that has gmail type animation in which when the circular avatar button is clicked the List tile gets selected with a rotation animation.
The list of items are built using ListView.builder. But when the circle avatar of a list item is clicked it affects the whole list of items and rotates every circle avatar. From similar questions asked I changed Listtile into a separate stateful widget as every item could hold its own state. But this doesn't change the output.
I didn't get the output even after using keys(Unique and object). I am stuck here for a long time but couldn't figure what and how to do it. Is there any way to differentiate widgets in Listview.builder and maintain their states seperately?
Alternatively when I use checkbox/selectable icon its holding the state for each item induvidually.
class MailList extends StatefulWidget {
int index;
List<MailModel> account;
Animation rotationAnim;
AnimationController circleAvatarController;
OneShotAnimation riveAnimationController1;
OneShotAnimation riveAnimationController2;
List<int> selectedIndexes = [];
UniqueKey key;
MailList({
required this.index,
required this.account,
required this.rotationAnim,
required this.circleAvatarController,
required this.riveAnimationController1,
required this.riveAnimationController2,
required this.selectedIndexes,
required this.key
}):super(key: key);
#override
_MailListState createState() => _MailListState();
}
class _MailListState extends State<MailList> {
#override
Widget build(BuildContext context) {
return Listener(
key: UniqueKey(),
child: Dismissible(
key: ValueKey(widget.index),
child: GestureDetector(
key: ValueKey(widget.index),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
shape: BoxShape.rectangle,
color: widget.account[widget.index].isSelected ? Colors.blue.withOpacity(0.2): null
),
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
GestureDetector(
child: AnimatedBuilder(
builder: (context, child){
return Transform(
alignment: Alignment.center,
child: CircleAvatar(
key: ValueKey(widget.index),
backgroundColor: widget.account[widget.index].isSelected ? widget.account[widget.index].defaultOnSelectedColor : widget.account[widget.index].senderInfo.profileColor.withOpacity(0.75),
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
builder: (context, double anim, child){
return widget.rotationAnim.value > 90 && widget.account[widget.index].isSelected
? Opacity(
child: Icon(
widget.account[widget.index].defaultOnSelectedIcon,
color: Colors.white,
),
opacity: anim,
)
: Text(
widget.account[widget.index].senderInfo.senderName[0].toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21.0,
color: Colors.white
));
},
duration: const Duration(seconds: 1),
),
),
transform: /*isSelected(widget.index) ? (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi))
: (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(0 / 180 * math.pi))*/
Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi),
);
},
animation: widget.circleAvatarController,
),
onTap: (){
selectUnSelect(widget.index);
widget.account[widget.index].isSelected = !widget.account[widget.index].isSelected;
widget.account[widget.index].isSelected ? widget.circleAvatarController.forward() : widget.circleAvatarController.reverse();
setState(() {
});
},
)
],
mainAxisAlignment: MainAxisAlignment.start,
),
const SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.account[widget.index].senderInfo.senderName,
style: TextStyle(
fontSize: 16.0,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
Text(
"10:00"
)
],
)
),
const SizedBox(
height: 5.0,
),
Text(
widget.account[widget.index].title,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.5,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
const SizedBox(
height: 5.0,
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Text(
widget.account[widget.index].content,
overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
child: Container(
child: widget.account[widget.index].starred ? Icon(
Icons.star,
color: Colors.orange[300],
):
Icon(
Icons.star_border
),
),
onTap: (){
setState(() {
widget.account[widget.index].starred = !widget.account[widget.index].starred;
});
},
)
],
),
),
const SizedBox(
height: 5.0,
),
],
),
)
],
),
),
),
onTap: (){
},
),
background: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: dragWidget(
left: 8.0,
controller: widget.riveAnimationController1
)),
],
),
secondaryBackground: Container(
alignment: Alignment.centerRight,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
right: 8.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
widget.riveAnimationController2
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
),
onDismissed: (direction){
widget.account.removeAt(widget.index);
},
),
);
}
bool isSelected(int index){
return widget.selectedIndexes.contains(index);
}
selectUnSelect(int index){
if(isSelected(index)){
widget.circleAvatarController.reverse();
Future.delayed(Duration(milliseconds: 320), (){
widget.selectedIndexes.remove(index);
setState(() {
});
});
}else{
widget.selectedIndexes.add(index);
}
}
Widget dragWidget({
double? left,
double? right,
required OneShotAnimation controller
}){
return Container(
alignment: right != null ? Alignment.centerRight : Alignment.centerLeft,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
left: left != null ? left : 0.0,
right: right != null ? right: 0.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
controller
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
);
}
} ```
// ListView.builder:
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index){
return MailList(
key: GlobalKey<_MailListState>(),
index: index,
account: account1_mail_data,
rotationAnim: rotationAnim,
circleAvatarController: circleAvatarController,
riveAnimationController1: _riveAnimationController1,
riveAnimationController2: _riveAnimationController2,
selectedIndexes: selectedIndexes
);
},
childCount: account1_mail_data.length
),
)
I'm trying to make listview using data from REST API, the position is below my image , but the layout reach its limit, so i can't create it , and showing this error
here my code, (Updated Code)
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:schoolofparentingalfa/assets/color/color.dart';
class Home extends StatefulWidget {
#override
Home2 createState() => Home2();
}
class Home2 extends State<Home> {
var colorsop = new Colorsop();
var apiconfig = new ApiConfig();
var apiClient = new ApiClient();
#override
void initState() {
super.initState();
}
//To call list from api
Future<List<Artikel>> getNews() async {
// future is used to handle the error when calling api > Future + async or await
var data = await http.get(
'https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=c4349a84570648eaa7be3cd673cc262b');
var jsonData = json.decode(data.body);
var newsData =
jsonData['articles']; //to retrieve data from articles array of api
List<Artikel> news = []; // create array
for (var data in newsData) {
//assign data into News model array list from articles array of api
Artikel newsItem = Artikel(
data['title'], data['description'], data['urlToImage']);
news.add(newsItem);
}
return news;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: <Widget>[
Positioned(
top: 0,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width,
color: Colors.yellow[800],
child: Align(
alignment: Alignment.center,
child: Text("Halo, Selamat Datang", style: TextStyle(color: Colors.white, fontSize: 25),))),
),
Positioned(
top: 90,
bottom: 0,
right: 0,
left: 0,
child: Container(
width: MediaQuery.of(context).size.width,
height: 600,
decoration: BoxDecoration(
color: Colors.white,
),
child: GridView.count(
crossAxisCount: 1,
children: [
Container( // Container 1
child: Row(
children: <Widget>[
Image.asset(
'lib/assets/image/kelas_online.png',
height: 120,
width: 150,
),
Image.asset(
'lib/assets/image/tanyaahli.png',
height: 120,
width: 150,
),
],
),
),
Container( // Container 2
child: Row(
children: <Widget>[
Image.asset(
'lib/assets/image/workshop_online.png',
height: 120,
width: 150,
),
Image.asset(
'lib/assets/image/MitraSekolah.png',
height: 120,
width: 150,
),
],
),
),
Container( //Container 3
margin: EdgeInsets.only(top: 15, bottom: 30, left: 20),
padding: EdgeInsets.symmetric(horizontal: 15),
child: FutureBuilder(
future: getNews(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//snapshot is same with response
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
// to retrieve data as all array indexes
itemBuilder: (BuildContext context, int index) {
// is same with holder
return InkWell(
// Inkwell is used to apply card view
onTap: () {
Artikel news = new Artikel(snapshot.data[index].post_link, snapshot.data[index].description, snapshot.data[index].post_image);//is used to onclick
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => new Details(news: news)
));
},
child: Card(
child: Row(
children: <Widget>[
Container(
width: 120.0,
height: 110.0,
child: ClipRRect(
//for corner radius
borderRadius:
BorderRadius.all(Radius.circular(8)),
//to retrieve image from array
child: snapshot.data[index].post_image == null
? Image.network(
'https://cdn2.vectorstock.com/i/1000x1000/70/71/loading-icon-load-icon-wait-for-a-wait-please-wait-vector-24247071.jpg')
: Image.network(
snapshot.data[index].post_image,
width: 100,
fit: BoxFit.fill,
),
),
),
Expanded(
child: ListTile(
//include title and subtitle
title: Text(snapshot.data[index].post_title),
subtitle: Text(snapshot.data[index].post_link == null
? 'Unknown Author'
: snapshot.data[index].post_link),
),
)
],
),
),
);
},
);
}
},
),
),
],
),
)
)
],),
);
}
}
}
class Details extends StatelessWidget{
final Artikel news;
Details({this.news}); // create constructor
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: Column(
children: <Widget>[
Stack( //little same with expanded
children: <Widget>[
Container(
height: 400,
child: Image.network('${this.news.post_image}',
fit: BoxFit.fill,),
),
AppBar(
backgroundColor: Colors.transparent,
leading: InkWell(
child: Icon(Icons.arrow_back_ios),
onTap: () => Navigator.pop(context),
),
elevation: 0,
)
],
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
SizedBox( // for title
height: 10,
),
Text(
'${this.news.post_title}',
style: TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 20,
letterSpacing: 0.2,
wordSpacing: 0.6
),
),
SizedBox( // for description
height: 20,
),
Text(
this.news.post_link,
style: TextStyle(
color: Colors.black54,
fontSize: 16,
letterSpacing: 0.2,
wordSpacing: 0.3
),
)
],
),
)
],
),
),
),
);
}
}
class Artikel {
final String post_title;
final String post_link;
final String post_image;
//Alt+insert > constructor
Artikel(this.post_title, this.post_link, this.post_image);
}
Can anyone help me?
Updated Screenshot .............................................................................
you can replace the code and check
return Scaffold(
body: Stack(children: <Widget>[
Positioned(
top: 0,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width,
color: Colors.yellow[800],
child: Align(
alignment: Alignment.center,
child: Text("Halo, Selamat Datang", style: TextStyle(color: Colors.white, fontSize: 25),))),
),
Positioned(
top: 90,
bottom: 0,
right: 0,
left: 0,
child: Container(
width: MediaQuery.of(context).size.width,
height: 600,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topLeft:Radius.circular(20), topRight:Radius.circular(20)) ),
child:
GridView.count(
crossAxisCount: 2,
children: List.generate(5, (index) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: GridTile(child: Image.network("https://upload.wikimedia.org/wikipedia/commons/6/6d/Good_Food_Display_-_NCI_Visuals_Online.jpg"),),
);
}) ,),)
)
],),
);
I am trying to use a dialog widget which will get some info from future builder, when I build the dialog under onTap it works but code looks so weird in that way because I use that build more then once, so I want to make a dialog class then call it when I needed but I couldn't achieve that yet, is this possible? My related code samples are below;
Code in the screen that I tried to call dialog box(homepage.dart):
void showCast() {
showDialog<void>(
context: context,
builder: (context){
return CastDialog();
}
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<Payload>(
...
...
...
child: GestureDetector(
child: Image.network(
snapshot.data
.movieCast[index]
.image != null
? "http://image.tmdb.org/t/p/w185" +
snapshot.data
.movieCast[index]
.image
: "http://image.tmdb.org/t/p/original/zUqyn3aQXTzeP1n8yd8Udt1twYA.jpg",
fit: BoxFit.contain,),
onTap: () async {
List<dynamic> castVar;
http.Response responseC = await http.get("http://lunedor.pythonanywhere.com/query?castid=${snapshot.data.movieCast[index].castID}&language=${AppLocalizations.of(context).translate('lan_code')}").timeout(const Duration(seconds: 10));
Map<String, dynamic> castV = jsonDecode(responseC.body);
castVar = castV['moviecastimages'];
print(castVar);
String nameC = (snapshot.data.movieCast[index]).toString();
String _movieCast = (snapshot.data.movieCast[index]).toString();
String _url = (snapshot.data.movieCast[index].castID).toString();
return
showCast();
},
),
Code for dialog(dialogs.dart):
class CastDialog extends StatefulWidget{
#override
_CastDialogState createState() => _CastDialogState();
}
class _CastDialogState extends State<CastDialog> {
String nameC;
String _movieCast;
String _url;
List castVar;
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return SimpleDialog(
title:
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(
width: 20,
height: 20,
child: GestureDetector(
onTap: () async{
http.Response responseB = await http.get("https://api.themoviedb.org/3/person/$_url?api_key=1b8cfaea32775f684a7baff93bb1a3fc&language=${AppLocalizations.of(context).translate('lan_code')}").timeout(const Duration(seconds: 10));
Map<String, dynamic> castCB = jsonDecode(responseB.body);
return showDialog(context: context,
builder: (context) {
return SimpleDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(nameC),
SizedBox(
width: 20,
height: 20,
child: GestureDetector(
onTap: (){ Navigator.pop(context);},
child: Icon(Icons.close)),
),
],
),
children: <Widget>[
Divider(
color: Colors.black.withOpacity(0.5),
height: 5,
thickness: 3,
),
Wrap(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: Text(castCB['biography'], style: TextStyle(fontSize: 14),),
),
),
],
),],
);
});
},
child: Icon(Icons.info)),
),
Text(_movieCast),
SizedBox(
width: 20,
height: 20,
child: GestureDetector(
onTap: (){ Navigator.pop(context);},
child: Icon(Icons.close)),
),
],
),
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.circular(15.0)),
children: <Widget>[
Divider(
color: Colors.black.withOpacity(0.5),
height: 5,
thickness: 3,
),
Material(
child: Container(
width: 400,
height: 600,
child: new GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.5 /
2.5,
),
itemCount: castVar.length,
itemBuilder: (context, index) {
return Column(
children: <
Widget>[
SizedBox(
height: 220,
child: Stack(
children: <Widget>[
Image.network(castVar[index]['poster'] != null ? "http://image.tmdb.org/t/p/w185" + castVar[index]['poster']
: "https://i.hizliresim.com/bbn0VB.jpg", fit: BoxFit.contain),
Container(
width: MediaQuery
.of(context)
.size
.width,
height: 220,
alignment: Alignment.bottomLeft,
padding: EdgeInsets.only(bottom: 5.0),
margin: const EdgeInsets.only(
left: 5.0),
child: new CircularPercentIndicator(
radius: 40.0,
lineWidth: 5.0,
percent: double.tryParse(
castVar[index]['vote'])/10,
center: Stack(
children: <Widget>[
Text(
castVar[index]['vote'],
style: TextStyle(
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = Colors.black,
),
),
new Text(castVar[index]['vote'],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.amber,),),
],
),
progressColor: Colors.amber,
)
),],
),
),
Text(castVar[index]['title'], maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold)
),
Text("(" + castVar[index]['year'] + ")", maxLines: 1,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,),
)
],
);
}),
),
),
],
);
}
}