Here is my code:
import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:transformer_page_view/transformer_page_view.dart';
class Body extends StatefulWidget {
#override
_Body createState() => _Body();
}
class _Body extends State<Body> {
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
bool _visible = false;
return FutureBuilder<String>(
future: rootBundle.loadString('assets/files/questions.csv'),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
List<List<dynamic>> csvTable =
CsvToListConverter().convert(snapshot.data);
return Scaffold(
appBar: AppBar(
title: Text("Flashcards"),
),
body: new TransformerPageView(
loop: true,
viewportFraction: 0.8,
transformer: new PageTransformerBuilder(
builder: (Widget child, TransformInfo info) {
return new Padding(
padding: new EdgeInsets.all(10.0),
child: new Material(
elevation: 8.0,
textStyle: new TextStyle(color: Colors.white),
borderRadius: new BorderRadius.circular(10.0),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new Positioned(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ParallaxContainer(
child: new Text(
csvTable[info.index + 1][0],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: FloatingActionButton(
onPressed: () {
// Call setState. This tells Flutter to rebuild the
// UI with the changes.
setState(() {
_visible = !_visible;
});
},
tooltip: 'Toggle Opacity',
child: Icon(Icons.flip),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: new Text(
csvTable[info.index + 1][1],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
),
position: info.position,
translationFactor: 400.0,
),
],
),
left: 10.0,
right: 10.0,
top: 100.0,
)
],
),
),
);
}),
itemCount: csvTable.length - 1),
);
});
}
}
The excel file has the following data in it:
Question,
Command to sort lines of text files?,Answer 1
What does the 3rd column of /etc/fstab show?,Answer 2
Alternative name and location for grub.conf on some systems?,Answer 3
What the standard BASH file descriptors?,Answer 4
The idea is that when I click the button the answer shows (Flashcard concept). However, clicking the button is not doing anything. Please for the answers, I have looked everywhere and I know that visibility can be used; however, I had the same behavior when I used it
I think this is because your visible bool flag is declared inside the build function. So every time you setState, the visible flag is set equal to false. You can move it as a global bool field, then it should keep what you change when you clicked the button.
class _Body extends State<Body> {
bool _visible = false; ///Put it here
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return FutureBuilder<String>(
future: rootBundle.loadString('assets/files/questions.csv'),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
List<List<dynamic>> csvTable =
CsvToListConverter().convert(snapshot.data);
return Scaffold(
appBar: AppBar(
title: Text("Flashcards"),
),
body: new TransformerPageView(
loop: true,
viewportFraction: 0.8,
transformer: new PageTransformerBuilder(
builder: (Widget child, TransformInfo info) {
return new Padding(
padding: new EdgeInsets.all(10.0),
child: new Material(
elevation: 8.0,
textStyle: new TextStyle(color: Colors.white),
borderRadius: new BorderRadius.circular(10.0),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new Positioned(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ParallaxContainer(
child: new Text(
csvTable[info.index + 1][0],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: FloatingActionButton(
onPressed: () {
// Call setState. This tells Flutter to rebuild the
// UI with the changes.
setState(() {
_visible = !_visible;
});
},
tooltip: 'Toggle Opacity',
child: Icon(Icons.flip),
),
position: info.position,
translationFactor: 300.0,
),
SizedBox(height: size.height * 0.05),
ParallaxContainer(
child: AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: new Text(
csvTable[info.index + 1][1],
style: new TextStyle(
fontSize: 24.0,
color: Colors.black,
),
),
),
position: info.position,
translationFactor: 400.0,
),
],
),
left: 10.0,
right: 10.0,
top: 100.0,
)
],
),
),
);
}),
itemCount: csvTable.length - 1),
);
});
}
}
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 have this application where I will have futurebuilder to get data first, and then return a scaffold with a separate splash screen to simulate the screen darken effect with alertdialog while enabling setstate (I tested, they dont allow setstate with alert dialog). However, when I used Animated Opacity, Color Tween or Animated Switcher, as soon as the screen loads, the app freezes.
code:
class MainPageState extends State<MainPage> with TickerProviderStateMixin {
GlobalKey imageKey = GlobalKey();
late Animation splashAnimation;
late AnimationController splashController;
double splashOpacity = 0;
#override
void initState() {
splashController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
splashAnimation =
ColorTween(begin: Colors.transparent, end: Color.fromARGB(155, 0, 0, 0))
.animate(splashController);
super.initState();
}
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
PageLoader pageLoader = context.watch<PageLoader>();
TabProperty tabProperty = context.watch<TabProperty>();
return WillPopScope(
onWillPop: () async {
//check if user logged in ? disable back btn : exit app
bool login = false;
await StorageManager.readData("loggedIn")
.then((value) => login = value);
if (login) {
return false;
} else {
SystemNavigator.pop();
}
return false;
},
child: FutureBuilder(
future: getMenuAndAccountInfo(pageLoader),
builder: ((context, snapshot) {
//placeholder
if (!snapshot.hasData) {
return Stack(children: [
SafeArea(
child: Scaffold(
body: pageLoader.currentPage,
),
),
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Color.fromARGB(100, 0, 0, 0),
),
Center(child: CircularProgressIndicator()),
]);
} else {
List<dynamic> menuData =
(snapshot.data as Map<String, dynamic>)["Menu"];
return SafeArea(
child: RepaintBoundary(
key: imageKey,
child: Stack(
children: [
Scaffold(
body: pageLoader.currentPage,
bottomNavigationBar: Container(
height: height * 0.07,
color: Colors.blue,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: MaterialButton(
padding: EdgeInsets.zero,
splashColor: Colors.orange,
highlightColor: Colors.transparent,
onPressed: () {
setState(() {});
splashOpacity = 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.bookmark,
color: Colors.white,
size: height * 0.035,
),
Text(
"Bookmark",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02),
),
],
)),
),
Expanded(
child: MaterialButton(
padding: EdgeInsets.zero,
splashColor: Colors.orange,
highlightColor: Colors.transparent,
onPressed: () {
setState(() {});
splashOpacity = 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.account_circle_rounded,
color: Colors.white,
size: height * 0.035,
),
Text(
"Profile",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02),
)
],
)),
),
],
),
),
),
Positioned.fill(
child: AnimatedOpacity(
opacity: splashOpacity,
duration: Duration(milliseconds: 400),
child: Container(
color: Color.fromARGB(155, 0, 0, 0),
),
),
)
],
),
),
);
}
}),
),
);
}
NEVER build the future as the future: parameter to the FutureBuilder!
future: getMenuAndAccountInfo(pageLoader),
No!
Instead, you want to declare a variable to hold the future in State, and in initState, initialize that variable, and in your FutureBuilder, reference that variable.
For details, see the first few paragraphs of the FutureBuilder documentation, or see my video on that.
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 have an algorithm working, and I need to click on the image to show it on the entire screen, I have not succeeded, I send all the code, add a comment "// THIS IS THE IMAGE I NEED TO LOOK BIG WHEN I CLICK" which is the image that I want to make it look large when I click it, I have tried in several ways but I have not been able to solve it, and the only thing I am missing is this functionality.
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:tienda/models/productos_model.dart';
import 'package:tienda/pages/otra_pagina.dart';
import 'package:tienda/pages/crear_productos.dart';
import 'package:tienda/pages/pedido_lista.dart';
import 'package:tienda/services/firebase_services.dart';
import 'package:tienda/widgets/header.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Flutter Demo',
theme: ThemeData(
//primarySwatch: Colors.yellow,
primaryColor: Colors.yellow[800],
),
home: MyHomePage(title: 'RETO SAN JOSE CHAPARRAL'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ProductosModel> _productosModel = List<ProductosModel>();
List<ProductosModel> _listaCarro = [];
FirebaseService db = new FirebaseService();
StreamSubscription<QuerySnapshot> productSub;
#override
void initState() {
super.initState();
_productosModel = new List();
productSub?.cancel();
productSub = db.getProductList().listen((QuerySnapshot snapshot) {
final List<ProductosModel> products = snapshot.documents
.map((documentSnapshot) =>
ProductosModel.fromMap(documentSnapshot.data))
.toList();
setState(() {
this._productosModel = products;
});
});
}
#override
void dispose() {
productSub?.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0, top: 8.0),
child: GestureDetector(
child: Stack(
alignment: Alignment.topCenter,
children: <Widget>[
Icon(
Icons.shopping_cart,
size: 38,
),
if (_listaCarro.length > 0)
Padding(
padding: const EdgeInsets.only(left: 2.0),
child: CircleAvatar(
radius: 8.0,
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Text(
_listaCarro.length.toString(),
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 12.0),
),
),
),
],
),
onTap: () {
if (_listaCarro.isNotEmpty)
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Cart(_listaCarro),
),
);
},
),
)
],
),
drawer: Container(
width: 170.0,
child: Drawer(
child: Container(
width: MediaQuery.of(context).size.width * 0.5,
//color: Colors.black, //color de menu principal
color: Colors.yellow[800],
child: new ListView(
padding: EdgeInsets.only(top: 30.0),
children: <Widget>[
Container(
height: 120,
child: new UserAccountsDrawerHeader(
accountName: new Text(''),
accountEmail: new Text(''),
decoration: new BoxDecoration(
image: new DecorationImage(
fit: BoxFit.fill,
image: AssetImage('assets/images/food1x.png'),
),
),
),
),
new Divider(),
new ListTile(
title: new Text(
'productos',
style: TextStyle(color: Colors.white),
),
trailing: new Icon(
Icons.fastfood,
size: 30.0,
color: Colors.white,
),
onTap: () =>
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) => CrearProductos(),
)),
),
new Divider(),
],
),
),
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
WaveClip(),
Container(
color: Colors.transparent,
padding: const EdgeInsets.only(left: 24, top: 48),
height: 170,
child: ListView.builder(
itemCount: _productosModel.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Row(
children: <Widget>[
Container(
height: 300,
padding: new EdgeInsets.only(
left: 10.0, bottom: 10.0),
child: Card(
elevation: 7.0,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(24)),
child: AspectRatio(
aspectRatio: 1,
child: CachedNetworkImage(
imageUrl:
'${_productosModel[index].image}' +
'?alt=media',
fit: BoxFit.cover,
placeholder: (_, __) {
return Center(
child:
CupertinoActivityIndicator(
radius: 15,
));
}),
),
),
),
],
);
},
))
],
),
Container(height: 3.0, color: Colors.grey),
SizedBox(
height: 5.0,
),
Container(
color: Colors.grey[300],
height: MediaQuery.of(context).size.height / 1.5,
child: GridView.builder(
padding: const EdgeInsets.all(4.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemCount: _productosModel.length,
itemBuilder: (context, index) {
final String imagen = _productosModel[index].image;
var item = _productosModel[index];
return Card(
elevation: 4.0,
child: Stack(
fit: StackFit.loose,
alignment: Alignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: CachedNetworkImage(
// THIS IS THE IMAGE I NEED TO LOOK BIG WHEN I CLICK
imageUrl:
'${_productosModel[index].image}' +
'?alt=media',
fit: BoxFit.cover,
placeholder: (_, __) {
return Center(
child:
CupertinoActivityIndicator(
radius: 15,
));
}),
),
Text(
'${_productosModel[index].name}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20.0),
),
SizedBox(
height: 15,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(
height: 25,
),
Text(
'${_productosModel[index].price.toString()}COP',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
color: Colors.black),
),
Padding(
padding: const EdgeInsets.only(
right: 8.0,
bottom: 8.0,
),
child: Align(
alignment: Alignment.bottomRight,
child: GestureDetector(
child: (!_listaCarro
.contains(item))
? Icon(
Icons.shopping_cart,
color: Colors.yellow[800],
size: 38,
)
: Icon(
Icons.shopping_cart,
color: Colors.red,
size: 38,
),
onTap: () {
setState(() {
if (!_listaCarro
.contains(item))
_listaCarro.add(item);
else
_listaCarro.remove(item);
});
},
),
),
),
],
)
],
),
],
));
},
)),
],
),
)),
));
}
}
Another solution is to show the larger image on the same screen using the dialog below.
InkWell(
onTap: () => showDialog(
builder: (BuildContext context) => AlertDialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.all(2),
title: Container(
decoration: BoxDecoration(),
width: MediaQuery.of(context).size.width,
child: Expanded(
child: Image.network(
'${_productosModel[index].image}' +
'?alt=media',
fit: BoxFit.fitWidth,
),
),
),
),
context: context),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
BaseUrl.host + posts!.imageUrl!,
fit: BoxFit.cover,
),
),
)
You can create a new page, wrap the image widget in a InkWell widget, and when tapped navigate to the new page showing the image.
EDIT:
Your widget image would become:
// THIS IS THE IMAGE I NEED TO LOOK BIG WHEN I CLICK
return InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => ImageScreen(
url: '${_productosModel[index].image}' + '?alt=media',
),
),
),
child: Expanded(
child: CachedNetworkImage(
imageUrl: '${_productosModel[index].image}' + '?alt=media',
fit: BoxFit.cover,
placeholder: (_, __) {
return Center(
child: CupertinoActivityIndicator(
radius: 15,
),
);
},
),
),
),
And then you create a new stateless widget which it builds a:
import 'package:flutter/material.dart';
class ImageScreen extends StatelessWidget {
final String? url;
ImageScreen({
Key? key,
#required this.url,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Expanded(
child: CachedNetworkImage(
imageUrl: '${this.url}',
// Ajust the image changing the box fit attributte
fit: BoxFit.cover,
placeholder: (_, __) {
return Center(
child: CupertinoActivityIndicator(
radius: 15,
),
);
},
),
),
);
}
}
After that when you click the app navigates to a new screen rendering only the image. You can add a back button later.
I want to get effect like this - when scrolled up, appbar is transparent with listview visible below it:
And scrolled down, only white color - first item below appbar:
My window layout:
return Container(
color: AppTheme.nearlyWhite,
child: SafeArea(
top: false,
bottom: false,
child: Scaffold(
backgroundColor: AppTheme.nearlyWhite,
body: Stack(
children: <Widget>[
DrawerUserController(
screenIndex: _drawerIndex,
drawerWidth: MediaQuery.of(context).size.width * 0.75,
animationController: (AnimationController animationController) => _sliderAnimationController = animationController,
onDrawerCall: (DrawerIndex drawerIndexdata) => _onDrawerCall(drawerIndexdata, _forceRefresh),
onDrawerTap:(DrawerIndex drawerIndexdata) => _onDrawerTap(drawerIndexdata),
screenView: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(8, MediaQuery.of(context).padding.top + 8, 8, 8),
child: _createAppBar(),
),
Expanded(
child:
Container(
color: Colors.white,
child: _screenView,
)
),
],
),
),
new FabDialer(_fabMiniMenuItemList, Colors.blue, new Icon(Icons.add))
],
),
),
),
);
}
_screenView is simple Listview().builder() and it shows InkWell widget for each item. My appbar is custom, defined like this:
_createAppBar() {
return SizedBox(
height: AppBar().preferredSize.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8, left: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
),
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child: Column(
children: <Widget>[
Text(
_menuSelected,
style: TextStyle(
fontSize: 22,
color: AppTheme.darkText,
fontWeight: FontWeight.w400,
),
),
Text(
globals.cityName,
style: TextStyle(
fontSize: 15,
color: AppTheme.darkerText,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 8, right: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
color: Colors.white,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius:
BorderRadius.circular(AppBar().preferredSize.height),
child: Icon(Icons.refresh, color: AppTheme.dark_grey,),
onTap: () => setState(() => _forceRefresh = true),
),
),
),
),
],
),
);
}
That's how it looks now with first list item visible:
So, almost there, but when scrolled down, appbar won't be transparent:
I tried to mess around with setting my appbar backround to color with transparency, without success. Also I need to get my widgets actually overlapped (ListView needs to overlap my appbar) and it generates error messages from Flutter.
Any ideas how to do that properly?
set extendBodyBehindAppBar: true in Scaffold widget. Then use Opacity widget like this,
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: Opacity( //Wrap your `AppBar`
opacity: 0.8,
child: AppBar(
title: Text("Demo"),
),
),
),
body: ListView.builder(
itemCount: 30,
itemBuilder: (context, index) {
return ListTile(
title: Text("Tile: $index"),
);
},
),
);
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Stack(
children:[
Container(
color:Colors.white,
padding:EdgeInsets.all(10),
child:ListView.builder(
itemCount:25+1,
//length + 1 is beacause to show 1st item at the beginning
shrinkWrap:true,
itemBuilder:(con,ind){
return ind==0 ?
Container(height:70)
:ListTile(
title:Text('Item $ind',
style:TextStyle(color:Colors.black,))
);
}
)
),
Container(
height:70,
color:Colors.transparent,
child:Card(
color:Colors.white.withAlpha(80),
child: Row(
children:[
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.list,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
),
Expanded(
flex:3,
child: Text('Title',
style:TextStyle(color:Colors.black,)),
),
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.search,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
),
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.more_vert,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
)
]
),
)
)
]
)
);
}