I'm building an application and I want to create an effect similar to this in a ListView.
https://css-tricks.com/recreating-the-facebook-messenger-gradient-effect-with-css/
If I would know the widget's position in the build method, I could calculate the gradient of the widget.
After the widget is rendered, I can get the position of the widget by a GlobalKey that is assigned to the widget in the build method. This approach isn't working in my case, because I need the position in order to render the widget with the correct gradient.
I used ColorFiltered Widget to make Facebook Messenger's gradient.
In a
Stack["gradient you want to apply", "ColorFiltered Widget which is parent of your ListView", ...]
put your Listview in ColorFiltered widget by child, pick filter, and you're done.
I used Firebase Cloud Firestore to make my messenger real-time so there's Streambuildter in my code.
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.pinkAccent,
Colors.deepPurpleAccent,
Colors.lightBlue,
],
),
),
),
Center(
child: Container(
alignment: Alignment.topCenter,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
controller: _scrollController,
reverse: true,
physics: ClampingScrollPhysics(),
child: StreamBuilder(
stream: Firestore.instance.collection('message').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
padding: EdgeInsets.all(100),
color: Colors.transparent,
child: CircularProgressIndicator(
backgroundColor: Colors.white,
),
);
}
return Column(
children: _chatmulticolorbubbles(snapshot.data),
);
},
),
),
),
),
This below makes chatbubblelist.
List<Widget> _chatmulticolorbubbles(data) {
List<Widget> list = [];
list.add(_dumpspace(10.0));
//print(data.documents[0]['chat'].toString());
var _wasme;
list.add(_chatbubble(
data.documents[0]['chat'], data.documents[0]['who'], false));
_wasme = data.documents[0]['who'];
for (var i = 1; i < data.documents.length; i++) {
if (data.documents[i]['who'] == true)
_wasme
? list.add(_chatbubble(
data.documents[i]['chat'], data.documents[i]['who'], true))
: list.add(_chatbubble(
data.documents[i]['chat'], data.documents[i]['who'], false));
else
_wasme
? list.add(_chatbubble(
data.documents[i]['chat'], data.documents[i]['who'], true))
: list.add(_chatbubble(
data.documents[i]['chat'], data.documents[i]['who'], false));
}
list.add(_dumpspace(80.0));
return list;
}
This is my GitHub of this project
Messenger Gradient Github
I hope it helped you!!
Related
............................................I am not asking about how a user can upload his/her images using authentication.
I am asking about how i can change my images inside the app using firestore(firebase) or how to change an title image inside the app by uploading an image from our desktop.
..............................................................................................................................................
This is my code
body: StreamBuilder(
stream: Firestore.instance.collection('Event').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
const Text('No Event');
}
else if(snapshot.hasError){ const Text('No data avaible right now'); }
else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot myEvent = snapshot.data.documents[index];
return ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
// scrollDirection: Axis.vertical,
children: <Widget>[
//1st box
Container(
margin: EdgeInsets.all(
SizeConfig.safeBlockHorizontal * 4),
child: Container(
child: new FittedBox(
child: Material(
// color: Colors.white,
elevation: 14.0,
borderRadius: BorderRadius.circular(24.0),
shadowColor: Color(0x802196F3),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
child: Padding(
padding: EdgeInsets.only(
left:
SizeConfig.safeBlockHorizontal *
4),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 8.0),
child: Container(
child: Text(
'${myEvent['date_1']}',
style: TextStyle(
color: Colors.black54,
fontSize: 24.0,
fontWeight:
FontWeight.bold),
)),
),
SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(
left: 8.0),
child: Container(
child: Column(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: <Widget>[
],
)),
),
],
),
),
),
Container(
width:
SizeConfig.safeBlockHorizontal * 60,
height:
SizeConfig.safeBlockHorizontal * 55,
child: ClipRRect(
borderRadius:
new BorderRadius.circular(24.0),
child: Image.network(
'${myEvent['image_1']}',
fit: BoxFit.fill,
alignment: Alignment.topRight,
),
),
),
],
),
),
),
),
),
If you have both your images and title inside firestore, then you need to update the database with the new title and image. To update the image, you need to reupload an image using the image_picker plugin:
Future getImage() async {
var firebaseUser = await FirebaseAuth.instance.currentUser();
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image != null) {
StorageReference ref = FirebaseStorage.instance.ref();
StorageTaskSnapshot addImg = await ref.child("image/img").putFile(image).onComplete;
if (addImg.error == null) {
final String downloadUrl =
await addImg.ref.getDownloadURL();
await Firestore.instance
.collection("user")
.document(firebaseUser.uid)
.updateData({"url": downloadUrl, "name": imageName});
}
}
}
So here you get a new image, add it to Firebase Storage and then using updateData() you can update both the image name and img url in the database.
I found an easiest way to change image in our app whenever we want: Step 1- use image network code as above Step 2- upload our images in fire base storage Step 3- Copy the image address and paste it in your Image field inside the collection , the image will automatically update in your app
My problem is when I wrap the image with a container that has a specific size and uses BoxFit.contain property for it will not round the image. checkout the below image:
I think the image cant round itself because it cants expand to fill container. I know that I can use BoxFit.cover but I want to use BoxFit.contain because I have limited space and images that can be of any sizes. my code:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
color: Colors.red,
height: 300,
width: 300,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://i.ytimg.com/vi/fq4N0hgOWzU/maxresdefault.jpg',
fit: BoxFit.contain,
),
),
),
),
);
}
You have to wrap your ClipRRect widget with Center or any Align widget.
Most of the widgets will try to fill its parent, if the parent doesn't specify any alignment property.
In your case the ClipRRect filled its parent Container (300x300) since the container doesn't specify any alignment to its child. And Image with contain property will try to maintain image ratio and being centered in ClipRRect widget. So It made ClipRRect corners invisible or no effect.
Demo: Dartpad
Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text("My image size"),
),
body: Center(
child: Container(
color: Colors.red,
height: 300,
width: 300,
child: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://mfiles.alphacoders.com/847/847991.jpg',
fit: BoxFit.contain,
),
),
),
),
),
)
Edit: Here I used Center widget. But you can also just simply specify alignment property in the Container widget.
Try using BoxFit.fill if i understood correctly this is what you want to achieve :
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text("My image size"),
),
body: Center(
child: Container(
color: Colors.red,
height: 300,
width: 300,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
'https://i.ytimg.com/vi/fq4N0hgOWzU/maxresdefault.jpg',
fit: BoxFit.fill,
),
),
),
),
);
}
Put in a codepen demo
EDIT for a workaround to your question :
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Future<Widget> getImage() async {
final Completer<Widget> completer = Completer();
final url = 'https://i.stack.imgur.com/lkd0a.png';
final image = NetworkImage(url);
final config = await image.obtainKey(const ImageConfiguration());
final load = image.load(config);
final listener = new ImageStreamListener((ImageInfo info, isSync) async {
print(info.image.width);
print(info.image.height);
completer.complete(Container(
child: Image(
image: image,
height: info.image.height.toDouble(),
width: info.image.width.toDouble(),
)));
});
load.addListener(listener);
return completer.future;
}
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text("My image size"),
),
body: Center(
child: FutureBuilder<Widget>(
future: getImage(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return snapshot.data;
} else {
return Text('LOADING...');
}
},
),
),
);
}
}
You can simply wrap the widget with a ClipOval with flutter.
Take a look at this doc : https://api.flutter.dev/flutter/widgets/ClipOval-class.html
use stack for it
Stack(
children: <Widget>[
],
)
Here, I’m simply using a container with an image inside it
Container(
decoration: Box Decoration(
image: Decoration Image(
image: Asset Image('images/new_york.jpg'),
fit: Box Fit . fit Height,
),
),
),
Basically i want to animate an AnimatedContainer's height between 2 values. But here is the problem. When my state is 1 i know the height so i can animate but when my state is 0 i want animated container to expand to available space. I tried to wrap my animated container with Expanded widget but that didn't work.
class _PreviewScreenState extends State<PreviewScreen> {
var selectedTab = 1;
#override
Widget build(BuildContext context) {
double imageWidth = MediaQuery.of(context).size.width;
double imageHeight = selectedTab == 1 ? imageWidth : null;
return Scaffold(
body: DefaultTabController(
length: 3,
initialIndex: selectedTab,
child: Background(
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('SHARE'),
),
Expanded(
child: AnimatedContainer(
height: imageHeight,
duration: Duration(milliseconds: 600),
color: Colors.red,
),
),
TabBar(
labelStyle: TextStyle(fontSize: 13),
indicator: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(40),
),
onTap: (index) {
setState(() {
selectedTab = index;
});
},
tabs: <Widget>[
Tab(child: Text('INSTAGRAM')),
Tab(child: Text('SQUARE')),
Tab(child: Text('OTHER'))
],
),
Container(
height: 100,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: ShareButton(
onPressed: () {},
),
),
),
)
],
),
),
),
),
);
}
}
You can use a Flexible widget instead of an Expanded widget. It gives the child "the flexibility to expand to fill the available space in the main axis, but, unlike Expanded, Flexible does not require the child to fill the available space."
Also, you should switch from AnimatedContainer to AnimatedSize as AnimatedContainer throws an error interpolating between double.infinity and a constant height.
So this
Expanded(
child: AnimatedContainer(
height: imageHeight,
duration: Duration(milliseconds: 600),
color: Colors.red,
),
),
will be come
Flexible(
child: AnimatedSize(
vsync: this,
duration: Duration(milliseconds: 600),
child: Container(
height: imageHeight,
color: Colors.red,),
),
),
For this to work, your _PreviewScreenState has to use the SingleTickerProviderStateMixin mixin and your imageHeight logic will have to change from null to double.infinity for the filling the available space.
i.e you will have:
class _PreviewScreenState extends State<PreviewScreen> with SingleTickerProviderStateMixin{
//rest of your code
}
and
double imageHeight = selectedTab == 1 ? imageWidth : double.infinity;
Here is a DartPad demonstration: https://dartpad.dev/bf4f969f76ab3092d0b1960bfdbf7825
I am new to animations in Flutter and did not figure out how to animate a widget movement between two states.
If I run the code below, the column's first child will go invisible and the RaisedButton will appear where the first widget was before.
How do I tell Flutter to animate this so that the RaisedButton will move upwards instead of just appearing there?
All solutions I found were way too complicated in my opinion...
bool visible = true;
Widget widget1, widget2;
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Visibility(
visible: visible,
child: widget1,
),
RaisedButton(
child: widget2,
onPressed: () => setState(() => visible = !visible),
),
],
)
I hope this helps
double height = 200;
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
AnimatedContainer(
duration: Duration(seconds: 3),
height: height,
child: Container(height: 200, width: 200, color: Colors.blue),
),
RaisedButton(
child: Container(height: 200, width: 200, color: Colors.yellow),
onPressed: () => setState(() => height = height == 0 ? 200 : 0),
),
],
);
}
Here is the result:
How can I change the background color of a flutter nav drawer?
There doesn't seem to be a color or background-color property.
When you build your ListView in the child property of your Drawer, you can wrap your different sections of the Drawer inside a Container and use the color property of the Container.
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new Container(child: new DrawerHeader(child: new CircleAvatar()),color: Colors.tealAccent,),
new Container (
color: Colors.blueAccent,
child: new Column(
children: new List.generate(4, (int index){
return new ListTile(
leading: new Icon(Icons.info),
);
}),
),
)
],
),
),
A better alternative if you already have a consistent coloring design in your mind, is to define your ThemeData under the theme property of the root of your app, the DrawerHeader and the body will follow your canvasColor, so you need to override the value of one of them to change the color:
return new MaterialApp(
....
theme: new ThemeData(
canvasColor: Colors.redAccent,
....),
)
Best way to wrap Drawer with Theme,
For example:
#override
Widget build(BuildContext context) {
return Scaffold(
//other scaffold items
drawer: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.blue, //This will change the drawer background to blue.
//other styles
),
child: Drawer(
child: Column(
children: <Widget>[
//drawer stuffs
],
),
),
);
}
The easiest way would probably be to just wrap the ListView inside a Container and specify its color like following:
drawer: Drawer(
child: Container(color: Colors.red,
child: new ListView(
...
)
)
)
For changing Drawer Header color use blow code
UserAccountsDrawerHeader(
accountName: Text("Ashish Rawat"),
accountEmail: Text("ashishrawat2911#gmail.com"),
decoration: BoxDecoration(
color: const Color(0xFF00897b),
),
currentAccountPicture: CircleAvatar(
backgroundColor: Theme.of(ctxt).platform == TargetPlatform.iOS
? const Color(0xFF00897b)
: Colors.white,
child: Text(
"A",
style: TextStyle(fontSize: 40.0),
),
),
),
You can just use this code;
drawer: Drawer(
child: Container(
//child: Your widget,
color: Colors.red,
width: double.infinity,
height: double.infinity,
),
)
PLAIN BACKGROUND
Just set your desired theme color using primarySwatch: Colors.brown property in ThemeData
class MyApp extends StatelessWidget {
final appTitle = 'Drawer Demo';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
theme: new ThemeData(
primarySwatch: Colors.brown, // Your app THEME-COLOR
),
home: MyHomePage(title: appTitle),
);
}
}
GRADIENT BACKGROUND
Add the gradient property to AppBar.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("profyl.org",
style: TextStyle(color: Colors.white),
textDirection: TextDirection.ltr),
flexibleSpace: Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
colors: [
const Color(0xFF3366FF),
const Color(0xFF00CCFF),
],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 0.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
),
),
body: HomeListPage(),
drawer: DrawerPage());
}
Try This.
#override
Widget build(BuildContext context) {
return Drawer(
child: Container(
color: Colors.black,
child: ListView(
padding: const EdgeInsets.all(0),
children: [
],
),
),
);
}
}
This will help
drawer: Drawer(
child: Container(
color: Colors.blueAccent,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
UserAccountsDrawerHeader(
decoration: BoxDecoration(
color: Color(0xFF56ccf2),
),
accountName: Text("User Name Goes"),
accountEmail: Text("emailaddress#gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor:
Theme.of(context).platform == TargetPlatform.iOS
? Color(0xFF56ccf2)
: Colors.white,
child: Text("TK",
style: TextStyle(fontSize: 50,
color: Colors.lightGreenAccent,),),
),
),
ListTile(
title: Text('Home',
style: TextStyle(
color: Colors.white,
fontSize: 18,
)),
contentPadding: EdgeInsets.fromLTRB(20, 5, 0, 5),
trailing: Icon(Icons.arrow_right,
color: Colors.white,),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => HomeScreen()));
},
),
],
),
),
),
The simplest way:
Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(color:Theme.of(context).bottomAppBarColor),
)],
),
)
You can wrap whatever you have in your drawer with a container wrapped with expanded widget. Thus you can change the color of the container there. Something like this will work.
Drawer(
child: Expanded(
child: Container(
color: Colors.red,
child: Text('Tabs'),
),
),
)