How to expand list items without affecting other items. (flutter) - flutter

I want to create the Netflix home page in flutter, and I'm stuck while creating this hover state.
I have created two base widgets. One for the number plus the thumbnail and the other one for the expanded view when the widget is hovered. Then I put them in a stack with an Inkwell where the onHover changes the state to show the expanded widget.
When I hover on the widget, it does switch between the normal state an expanded state, the problem comes when I try to put a list of these widgets together.
When using row (or ListView) to put them together, after hovering, the expanded widget makes the other widgets move. (which is not the wanted behaviour, I want them to overlap)
When I use it with stack, the widgets do overlap but now it isn't scrollable anymore.
I have added the link to the repo for anyone that wants to clone it and try running it themselves, I'm running it on flutter web.
https://github.com/Advait1306/netflix-flutter
Widget with thumbnail and number:
class TopListItem extends StatelessWidget {
final int index;
const TopListItem({Key? key, required this.index}) : super(key: key);
#override
Widget build(BuildContext context) {
const double height = 250;
return SizedBox(
height: height,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset("assets/numbers/$index.svg",
fit: BoxFit.fitHeight, height: height),
Transform.translate(
offset: const Offset(-30, 0),
child: Image.asset("assets/thumbnails/thumb1.jpg"))
],
),
);
}
}
Expanded view widget:
import 'package:flutter/material.dart';
class HoverMovieTrailer extends StatelessWidget {
const HoverMovieTrailer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const textTheme = TextStyle(color: Colors.white);
return SizedBox(
width: 400,
height: 400,
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: const Color(0xFF242424)),
child: Column(
children: [
Image.asset("assets/backgrounds/background1.jpg"),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: const [
RoundIconButton(icon: Icons.play_arrow_outlined),
SizedBox(width: 5),
RoundIconButton(icon: Icons.add_outlined),
SizedBox(width: 5),
RoundIconButton(icon: Icons.thumb_up_alt_outlined),
SizedBox(width: 5),
],
),
Row(
children: const [
RoundIconButton(icon: Icons.keyboard_arrow_down_outlined),
],
),
],
),
),
const SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"98% Match",
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.bold
),
),
const SizedBox(width: 5),
Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1)
),
child: const Text(
"18+",
style: textTheme,
),
),
const SizedBox(width: 5),
const Text(
"4 Seasons",
style: textTheme,
),
const SizedBox(width: 5),
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 1)
),
child: const Text(
"HD",
style: textTheme,
),
)
],
),
),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Captivating",
style: textTheme,
),
const SizedBox(width: 5),
Container(
width: 5,
height: 5,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white54
),
),
const SizedBox(width: 5),
const Text(
"Exciting",
style: textTheme,
),
const SizedBox(width: 5),
Container(
width: 5,
height: 5,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white54
),
),
const SizedBox(width: 5),
const Text(
"Docuseries",
style: textTheme,
),
],
),
),
],
),
),
);
}
}
class RoundIconButton extends StatelessWidget {
final IconData icon;
const RoundIconButton({Key? key, required this.icon}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(width: 2, color: Colors.white)),
margin: const EdgeInsets.all(1),
child: IconButton(
onPressed: () {},
icon: Icon(icon),
color: Colors.white,
),
);
}
}
Combining the widgets in the single widget:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:netflix_flutter/widgets/hover_movie_trailer.dart';
import 'package:netflix_flutter/widgets/top_list_item.dart';
class TopListItemWithHover extends StatefulWidget {
const TopListItemWithHover({Key? key}) : super(key: key);
#override
State<TopListItemWithHover> createState() => _TopListItemWithHoverState();
}
class _TopListItemWithHoverState extends State<TopListItemWithHover> {
bool hover = false;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: (){},
onHover: (value){
log("Hover value: $value");
setState(() {
hover = value;
});
},
child: Stack(
clipBehavior: Clip.none,
children: [
TopListItem(index: 1),
if(hover) HoverMovieTrailer(),
],
),
);
}
}
Lists:
import 'package:flutter/material.dart';
import 'package:netflix_flutter/widgets/hover_movie_trailer.dart';
import 'package:netflix_flutter/widgets/top_list_item.dart';
import 'package:netflix_flutter/widgets/top_list_item_with_hover.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 400,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
itemCount: 8,
itemBuilder: (context, index) {
return TopListItemWithHover();
},
),
),
),
const SizedBox(height: 50),
SingleChildScrollView(
child: SizedBox(
height: 400,
child: Stack(
fit: StackFit.passthrough,
children: [
for (var i = 10; i >= 0; i--)
Positioned(
left: (i) * 300,
child: TopListItemWithHover(),
)
],
),
),
)
],
),
);
}
}

So finally found the solution to this problem, the way to move forward is to use a stack in SingleChildScrollView.
A mistake that I made is, I did not set the SingleChildScrollView's direction to horizontal. So I added that.
And then one more addition that's needed is -- A empty sized box which has the width of sum of all the items in the stack.
Stack(
clipBehavior: Clip.none,
children: [
const SizedBox(
width: 300*10,
),
for (var i = 10; i >= 0; i--)
Positioned(
left: (i) * 300,
child: TopListItemWithHover(),
)
],
)
This finally expanded the stack to the required width and then made is scrollable also.

Related

flutter - PageView Builder & Card - displaying different info on each card

I am trying to use PageView and Card. My objective is to display different information on the fourth cards that the user can see.
To be more precise, on one card, I could display a title for example. On the second card, I will display dates, on the third card, it could be some random text...
I hope this is clear.
To give you more color on this, the information will come from FireBase.
I do not know how to do this easily. I have considered to create 4 widget myCard, with different info on it and to use them. But I would like to do something more professional and efficient. Thank you
About FireBase, I do not use json. I have a simple document in FireBase. In this document, I have several fields, like 'price' 'quantity' 'reference'...
I just want that each card to display two or three fields from this FireBase document.
class MyBodyWindMill extends StatefulWidget {
const MyBodyWindMill({Key key}) : super(key: key);
#override
_MyBodyWindMillState createState() => _MyBodyWindMillState();
}
class _MyBodyWindMillState extends State<MyBodyWindMill> {
#override
Widget build(BuildContext context) {
return
Container(
height: 260,
child: PageView.builder(controller: PageController(viewportFraction: 0.88),
itemCount: 4,
itemBuilder: ( _, i){
return GestureDetector(
child: Container(
padding: const EdgeInsets.only(left:20, top:20),
height: 220,
width: MediaQuery.of(context).size.width-20,
margin: const EdgeInsets.only(right:10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
),
child:Column(
children: [
MyCard(),
],
))
);
},
));
}}
class MyCard extends StatefulWidget {
const MyCard( {Key key} ) : super(key: key);
#override
_MyCardState createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
//String monText;
#override
Widget build(BuildContext context) {
return Container(
height: 230,
child: Stack(
children: [
Positioned(
top: 35,
left: 20,
child: Card(
elevation: 6.0,
shadowColor: Colors.grey,
child: Container(
height: 180,
width: 0.9,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(0.0),
boxShadow: [BoxShadow(
color: Colors.black.withOpacity(0.3),
offset: Offset(-10.0, 10.0),
blurRadius: 20.0,
spreadRadius: 4.0,
),
])
),
),
),
Positioned(
top: 0.0,
left: 30.0,
child:Card(
elevation: 10.0,
shadowColor: Colors.grey.withOpacity(0.5),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Container(
height: 200,
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
image: DecorationImage(
fit: BoxFit.fill,
image:AssetImage('assets/header.jpg'),
),
),
))),
Positioned(
top:40,
left:200,
child:
Container(
height: 150,
width: 170,
child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text('Title: ', style: TextStyle(fontSize:16,
color:Colors.red),),
Divider(color:Colors.black),
Text('Info 1: ', style: TextStyle(fontSize:16,
color:Colors.red),),
Text('Info 2'),
])
))
],
),
);
}
}
Based on what you're trying to do it looks like what you need is something like this widget:
class InfoBlock {
final String title;
final String description;
InfoBlock({
required this.title,
required this.description,
});
}
class MyCard extends StatefulWidget {
String title;
List<InfoBlock> infoBlocks;
MyCard({
Key? key,
required this.title,
required this.infoBlocks,
}) : super(key: key);
#override
_MyCardState createState() => _MyCardState();
}
class _MyCardState extends State<MyCard> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
image: const DecorationImage(
fit: BoxFit.fill,
image: AssetImage('header.jpeg'),
))),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: const TextStyle(fontSize: 30),
),
...widget.infoBlocks.map((block) {
return Text(
'${block.title}: ${block.description}',
style: const TextStyle(fontSize: 16),
);
}),
],
)
],
),
);
}
}
And then you can use it like-so:
Column(
children: [
MyCard(
title: 'Quantity',
infoBlocks: [
InfoBlock(title: 'Price', description: '100'),
],
),
MyCard(
title: 'Status',
infoBlocks: [
InfoBlock(title: 'Txt1', description: 'value1'),
InfoBlock(title: 'Txt2', description: 'value2'),
InfoBlock(title: 'Txt3', description: 'value3'),
],
),
],
));
And here's what the output would look like:
Here's a a link to Flutlab with the above code fully functional: https://flutlab.io/ide/6d8762de-50b1-4cfe-a509-a301edaacebf

How to build this view for n number of removable cards?

Which widget would be best to use? If listview.builder then can you please share a sample code? This is the UI.
GridView is the widget you're looking for. Just use GridView.count will give you the desired result
Implementation of GridView.count()
#override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
children: List.generate(10, (index) => const DemoCard()));
}
And here the full example:
import 'package:flutter/material.dart';
class TestPage extends StatelessWidget {
const TestPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
children: List.generate(10, (index) => const DemoCard()));
}
}
class DemoCard extends StatelessWidget {
const DemoCard({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(8),
width: 200,
height: 200,
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20))),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icons.developer_mode,
color: Colors.blue,
size: 50,
),
const SizedBox(height: 10),
Text(
'Demo Text',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 2),
Text(
'Sub-Demo Text',
style: Theme.of(context).textTheme.titleMedium!.copyWith(
fontWeight: FontWeight.w600, color: Colors.lightBlue),
),
const Expanded(child: SizedBox()),
Row(
children: [
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: TextButton(
child: const Text('Edit Privilage'),
onPressed: () {},
),
),
),
const Icon(Icons.delete, color: Colors.red),
],
),
],
),
),
),
);
}
}
The result would be:
You can implement it in two ways:
GridView Widget
flutter_staggered_grid_view Package

How to add highlight overlay around the widget upon click?

I'm working on simple design system using Flutter. I want to highlight a widget upon selection (click), as you can see in the below images button get highlighted upon click. It gets handle and border.
Challenging part: I don't want layout getting changed as additional space taken by handle and border upon click. I want widget, handle and border are overlaid, so that it wouldn't shift the position of other neighbouring widgets.
And after selection
You could also use a Stack with the overlay bleeding out of the Stack thanks to a clipBehavior of Clip.none.
Full code
Just copy paste it in a DartPad to see it in action.
import 'package:flutter/material.dart';
const kcPrimary = Color(0xFF001989);
const kcSecondary = Color(0xFF239689);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
PlayButton(),
Text('Blablablablabla'),
Text('Blablablablabla'),
Text('Blablablablabla'),
],
),
),
),
);
}
}
class PlayButton extends StatefulWidget {
const PlayButton({Key? key}) : super(key: key);
#override
State<PlayButton> createState() => _PlayButtonState();
}
class _PlayButtonState extends State<PlayButton> {
bool clicked = false;
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
InkWell(
onTap: () => setState(() => clicked = !clicked),
child: _mainButton,
),
if (clicked) ...[
Positioned.fill(
child: IgnorePointer(
child: _overlayBorder,
),
),
Positioned(
top: -20.0,
left: 0,
child: _overlayTitle,
),
Positioned(top: 0, right: 0, child: _corner),
Positioned(bottom: 0, right: 0, child: _corner),
Positioned(bottom: 0, left: 0, child: _corner),
Positioned(top: 0, left: 0, child: _corner),
],
],
);
}
Widget get _mainButton => Container(
width: 80.0,
height: 40.0,
decoration: BoxDecoration(
border: Border.all(
color: kcPrimary,
width: 3.0,
),
borderRadius: const BorderRadius.all(Radius.circular(12))),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.play_arrow),
Text('Play'),
],
),
),
);
Widget get _overlayBorder => Container(
decoration: BoxDecoration(
border: Border.all(
color: kcSecondary,
width: 3.0,
),
),
);
Widget get _corner => Container(width: 10, height: 10, color: kcSecondary);
Widget get _overlayTitle => Container(
height: 20.0,
width: 48.0,
color: kcSecondary,
alignment: Alignment.center,
child: const Text(
'Button',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
);
}

Flutter - How do I scroll down a Card

My app currently looks like this:
And the here's the code behind it:
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.lightBlue),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: [
Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24))),
elevation: 30,
child: Padding(
padding: EdgeInsets.only(top: 120, right: 15, bottom: 20, left: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextField(
controller: TextEditingController(text: '12121+1212'),
keyboardType: TextInputType.none,
textAlign: TextAlign.end,
style: TextStyle().copyWith(fontSize: 70),
decoration: InputDecoration(border: InputBorder.none),
),
Align(
alignment: Alignment.centerRight,
child: Text(
'Result',
style: TextStyle().copyWith(fontSize: 36),
),
),
Container(
height: 6,
width: 25,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.all(Radius.circular(14))),
)
],
)),
),
Expanded(child: Container())
]))));
}
}
Now, I want to scroll down the this card to reveal a list above it,something like this:
https://youtu.be/qZtGjd_-KwI
I think it could be done with CustomScrollView, but as a novices CustomScrollView and Slivers are a bit complex for me to understand. A little help regrading how to go about this would be appreciated.
I think you want a CustomScrollView. This will get you close (but won't collapse the main body as in your video example). Note if you provide a hasScrollBody to true then you're body will shrink in response but it'll then overflow. Even a ConstrainedBox with minimum height didn't work for me so that shrinking part is really the hardest part.
class MyHomePage extends StatelessWidget {
MyHomePage({Key? key}) : super(key: key);
final centerKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
center: centerKey,
slivers: [
const SliverAppBar(title: Text('The real title')),
SliverList(
delegate: SliverChildListDelegate.fixed(
[
Container(
color: Colors.pink,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: double.infinity,
height: 200.0,
color: Colors.pink,
),
Container(
width: double.infinity,
height: 200.0,
color: Colors.blue,
),
Container(
width: double.infinity,
height: 200.0,
color: Colors.red,
),
],
),
),
],
),
),
SliverAppBar(
key: centerKey,
expandedHeight: 100.0,
backgroundColor: Colors.transparent,
flexibleSpace: const Card(
margin: EdgeInsets.zero,
elevation: 14.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(32.0),
),
),
child: SizedBox.expand(),
),
),
SliverFillRemaining(
hasScrollBody: false,
child: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: List.generate(5, (i) => i).map((i) {
return Expanded(
child: Row(
children: List.generate(4, (i) => i).map((i) {
return const Expanded(
child: Placeholder(),
);
}).toList(),
),
);
}).toList(),
),
),
),
),
],
),
);
}
}
The main part you should concern yourself with is the centerKey. That tells the CustomScrollView where it should start from. You pass that to both the CustomScrollView and the SliverAppBar.
The SliverFillRemaining will fill what's left of the viewport, which if you're starting at the SliverAppBar will be about 80%. FillRemaining will take you into the box model (as opposed to the Sliver model) which allows you to just use the space as you regularly would with columns and what not.

Is it possible to update PopupMenuButton items while it is still open?

Hello to everyone reading this,
So i've been trying to get a PopupMenuButton to change the current selected tile while it is still open. I'm using it in a drawer so a user can easily change their status. I've trimmed my code a bit to provide some basic replicable code. It uses a cubit to manage the users 'status'. The status does update, but the selection in the PopupMenuButton does not. Is there an easy way to fix this?
This is how it looks now:
status change
I've already tried:
Wrapping the child of the PopupMenuItem in a StatefulBuilder and making the PopupMenuButton a stateful widget
Wrapping the child of the PopupMenuItem in a BlocConsumer
Wrapping the child of the PopupMenuItem in a container and giving it a color based the cubit status
Giving the PopupMenuItem a ListTile as a child and setting its selected property based on the cubit status
Making the PopupMenuButton a stateful widget and managing the status using setState
This is my (simplified) code:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DrawerStatusTestScaff extends StatelessWidget {
const DrawerStatusTestScaff({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => UserStatusCubit(),
child: Scaffold(
appBar: AppBar(),
drawer: MenuDrawer(),
),
);
}
}
class MenuDrawer extends StatelessWidget {
const MenuDrawer({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final double _headerHeight = kToolbarHeight +
((3 * (kToolbarHeight - 20)) + 40) +
MediaQuery.of(context).padding.top;
void _toSettings() {
print('Settings pressed');
Navigator.pop(context);
}
return Container(
width: 305,
child: Drawer(
child: Column(
children: [
SizedBox(
height: _headerHeight,
child: UserAccountsDrawerHeader(
margin: const EdgeInsets.all(0),
currentAccountPicture: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFFFFFFFF),
),
child: Center(
child: const Text('S.P',
style: TextStyle(
fontSize: 30,
color: const Color(0xFF03A9F4),
)),
),
),
accountName: const Text('S.O.M.E Person'),
accountEmail: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
StatusPopUp(
child: Row(
children: [
const Text('s.person#companymmail.com'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: Icon(
Icons.circle,
color: context.watch<UserStatusCubit>().state == 0
? const Color(0xFF00E676)
: context.watch<UserStatusCubit>().state == 1
? const Color(0xFFFFEE58)
: context
.watch<UserStatusCubit>()
.state ==
2
? const Color(0xFFE53935)
: const Color(0xFFBDBDBD),
size: 10,
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(right: 15),
child: Tooltip(
message: 'Settings',
child: InkWell(
onTap: _toSettings,
child: const Icon(
Icons.settings,
size: 25,
),
),
),
),
],
),
decoration: BoxDecoration(
color: const Color(0xFF03A9F4),
),
),
),
Expanded(
child: ListView(),
),
],
),
),
);
}
}
class StatusPopUp extends StatelessWidget {
final Widget child;
const StatusPopUp({
Key? key,
required this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final int _dropDownIndex = context.watch<UserStatusCubit>().state;
return PopupMenuButton<int>(
tooltip: 'Change Status',
offset: Offset(0, 51.5 + (_dropDownIndex.toDouble() * 48.5)),
onSelected: (int index) {
context.read<UserStatusCubit>().emit(index);
},
initialValue: _dropDownIndex,
child: child,
itemBuilder: (context) => <PopupMenuEntry<int>>[
PopupMenuItem<int>(
value: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Online'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFF00E676),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Busy'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFFFEE58),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Away'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFE53935),
size: 10,
),
),
],
),
),
PopupMenuItem<int>(
value: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Offline'),
Padding(
padding: const EdgeInsets.fromLTRB(15, 2, 0, 0),
child: const Icon(
Icons.circle,
color: const Color(0xFFBDBDBD),
size: 10,
),
),
],
),
),
],
);
}
}
class UserStatusCubit extends Cubit<int> {
UserStatusCubit() : super(0);
}