Flutter :- How to sorting a custom List - sorting

Here I want I've some listed item and I want to sort item them on its id or rating. when I click on the side drawer button it will need to rearrange the list view with sorted items hope you understand my question.
I've tried this smooth_sort package for sorting but it won't work for me because it will sort based on the index. and I want to sort based on my own value.
Here is the output image
Here is my code
class _MapAddressListingScreenState extends State<MapAddressListingScreen>
with AfterLayoutMixin<MapAddressListingScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
List addressData;
#override
void afterFirstLayout(BuildContext context) {
_getAddress();
}
_getAddress() {
List requestedLatlong = ModalRoute.of(context).settings.arguments;
print("============================Address Data");
print(requestedLatlong);
setState(() {
addressData = requestedLatlong;
});
}
List<Widget> _addressData() {
List<Widget> _address = [];
for (int i = 0; i < addressData.length; i++) {
_address.add(
GestureDetector(
onTap: () {
print('address list taped');
AddWalkinModel model = widget.addWalkinModel;
print("After model");
model.fromMap = false;
model.fromMapListing = true;
model.autoAssign = addressData[i]['autoAssign'];
model.branchId = addressData[i]['branchId'];
model.branchServiceId = addressData[i]['branchServiceId'];
print("After Model Model");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DynamicScreen(
addWalkinModel: model,
),
),
);
},
child: AddressListingTile(listingTileData: addressData[i]),
),
);
}
return _address;
}
Widget itemCard() {
return Container(
child: Column(
children: _addressData(),
),
);
}
#override
Widget build(BuildContext context) {
var colorStyles = Theming.colorstyle(context);
return Scaffold(
key: _scaffoldKey,
backgroundColor: colorStyles['primary'],
appBar: AppBar(
title: Text("BSP Listview "),
centerTitle: true,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
},
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.sort),
onPressed: () {
_scaffoldKey.currentState.openEndDrawer();
},
),
],
),
endDrawer: Drawer(
child: Column(
children: <Widget>[
SizedBox(
height: 100,
),
FlatButton(
onPressed: () {
ratingSort.shuffle();
},
child: Text("sort")),
],
),
),
body: Stack(
children: <Widget>[
Container(
child: ClipRRect(
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(30.0),
topRight: const Radius.circular(30.0)),
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
itemCard(),
],
),
),
)
],
),
),
),
],
),
);
}
}

You can use the sort method of the list for sorting the list in the specific format as follows
#override
void initState() {
super.initState();
for(int i=0;i<fields.length;i++) {
print("BEFORE==>>> ${fields[i].rating}");
}
fields.sort((a, b) => a.rating.compareTo(b.rating)); ///FROM THIS LINE YOU CAN PERFORM SHORTING ACCORDING TO THE RATING BASES
for(int i=0;i<fields.length;i++) {
print("AFTER==>>> ${fields[i].rating}");
}
}
And i have use the my demo custom list for it
List<Fields> fields = [
new Fields(
'DEFAULT CATEGORY',
2,
),
new Fields(
'DEFAULT CATEGORY',
1,
),
new Fields(
'DEFAULT CATEGORY',
5,
),
new Fields(
'DEFAULT CATEGORY',
3,
),
new Fields(
'DEFAULT CATEGORY',
4,
),
];
And output will be follow
Please check the full source of code for it
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutterlearningapp/colors.dart';
class HomeScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _HomeScreen();
}
}
class _HomeScreen extends State<HomeScreen> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
appBar: AppBar(
title: Text("Demo Scroll"),
),
body: Container(
height: double.infinity,
width: double.infinity,
color: Colors.white,
child: Column(
children: <Widget>[
RaisedButton(
child: Text("Shoring"),
onPressed: (){
setState(() {
fields.sort((a, b) => a.rating.compareTo(b.rating));
});
},
),
Expanded(
child: ListView.builder
(
itemCount: fields.length,
itemBuilder: (BuildContext ctxt, int index) {
return ListTile(
title: new Text("Rating #${fields[index].rating}"),
subtitle: new Text(fields[index].title),
);
}
) ,
)
],
),
),
));
}
}
class Fields {
final String title;
final int rating;
Fields(this.title, this.rating);
}
List<Fields> fields = [
new Fields(
'Two',
2,
),
new Fields(
'One',
1,
),
new Fields(
'DEFAULT CATEGORY',
5,
),
new Fields(
'Three',
3,
),
new Fields(
'Four',
4,
),
];
And above output of the program as follow

Related

Flutter dropdown, select is disabled on validation error

Validation is working well but incase of validation error, option to select on option is disabled.
DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null)
return "Please select your gender";
return null;
},
),
Above code is in a page view,
my variables
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
My entire Form code : just reduced to one field its very long
class SignUp extends StatefulWidget {
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final List<GlobalKey<FormState>> _page = [
GlobalKey<FormState>(),
GlobalKey<FormState>(),
GlobalKey<FormState>(),
];
final _key = GlobalKey<ScaffoldState>();
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
void changePage() {
if (currentPageValue < 3) {
if (_page[currentPageValue].currentState.validate()) {
setState(() {
currentPageValue += 1;
});
}
}
}
#override
Widget build(BuildContext context) {
var deviceSize = MediaQuery.of(context).size;
var deviceWidth = deviceSize.width;
return Scaffold(
key: _key,
backgroundColor: _backgroundColor,
appBar: AppBar(
backgroundColor: _backgroundColor,
leading: IconButton(
icon: Icon(
currentPageValue == 0 ? Icons.close : Icons.keyboard_backspace,
size: 20.0,
color: _headerColor,
),
onPressed: currentPageValue == 0
? () => Navigator.pop(context)
: () => back()),
centerTitle: true,
elevation: 0.0,
),
body: Form(
key: _page[0],
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: deviceWidth * 0.9,
height: dropDownHeight,
child: ButtonTheme(
child: DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null) return "Please select your gender";
return null;
},
),
),
),
RaisedButton(
color: buttonColor,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0),
),
child: Text(
'Next',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
),
),
onPressed: () => changePage(),
),
],
),
),
),
);
Looking at your code, I strongly recommend you create 3 different Forms and validate them separately, so you don't have any problem regarding form state. To achieve this, you can just wrap the fields into their respective Form and be sure not to mix the forms nor have them one inside the other.
Below, an example based on your code and on what I have said:
class SignUp extends StatefulWidget {
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _page1 = GlobalKey<FormState>();
final _page2 = GlobalKey<FormState>();
final _page3 = GlobalKey<FormState>();
final _key = GlobalKey<ScaffoldState>();
int currentPageValue;
List<String> _gender = ['Male', 'Female'];
String _selectedGender;
void changePage(GlobalKey<FormState> page) {
if (currentPageValue < 3) {
if (page.currentState.validate()) {
setState(() {
currentPageValue += 1;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
body: ListView(
children: <Widget>[
Form(
key: _page1,
child: Column(
children: [
DropdownButtonFormField(
isExpanded: true,
hint: Text('Gender'),
value: _selectedGender,
onChanged: (newValue) {
setState(() {
_selectedGender = newValue;
});
},
items: _gender.map((gender) {
return DropdownMenuItem(
child: new Text(gender),
value: gender,
);
}).toList(),
validator: (value) {
if (value == null) return "Please select your gender";
return null;
},
),
RaisedButton(
child: Text('Next'),
onPressed: () => changePage(_page1),
),
],
),
),
Form(
key: _page2,
child: Column(
children: [
// SomeField(
//
// ),
// SomeOtherField(
//
// ),
RaisedButton(
child: Text('Next'),
onPressed: () => changePage(_page2),
),
],
),
),
],
),
);
}
}

Flutter, Carousel Text change with Image change

Row(
children: <Widget>[
new Expanded(
child: SizedBox(
height: 150.0,
width: 300,
child: Carousel(
boxFit: BoxFit.cover,
autoplay: true,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds: 1000),
dotSize: 6.0,
dotIncreasedColor: Colors.grey,
dotBgColor: Colors.transparent,
dotPosition: DotPosition.bottomCenter,
dotVerticalPadding: 10.0,
showIndicator: true,
indicatorBgPadding: 7.0,
images: [
AssetImage('assets/mountain.jpg'),
AssetImage('assets/mountain2.png')
],
),
),
),
// Other children below
]
)
Hi, I've managed to implement a carousel but I am wondering how you can add Text to it on the center. When the carousel moves to a new image I want the text to change to. I am using carousel_pro 1.0.0.
You can copy paste run full code below
image attribute actually can use widget , so you can use Card
code snippet
images: [
Card(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle:
Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
Image.network(
'https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'),
])),
working demo
full code
import 'package:flutter/material.dart';
import 'package:carousel_pro/carousel_pro.dart';
class CarouselPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 300.0,
width: 300.0,
child: Carousel(
boxFit: BoxFit.cover,
autoplay: false,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds: 1000),
dotSize: 6.0,
dotIncreasedColor: Color(0xFFFF335C),
dotBgColor: Colors.transparent,
dotPosition: DotPosition.topRight,
dotVerticalPadding: 10.0,
showIndicator: true,
indicatorBgPadding: 7.0,
images: [
Card(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle:
Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
Image.network(
'https://cdn-images-1.medium.com/max/2000/1*GqdzzfB_BHorv7V2NV7Jgg.jpeg'),
])),
Card(
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle:
Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
Image.network(
'https://cdn-images-1.medium.com/max/2000/1*wnIEgP1gNMrK5gZU7QS0-A.jpeg'),
])),
],
),
),
),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CarouselPage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
This is complete working code with Image on top with text and dots. For this you need to use these two libraries:- " carousel_slider: ^4.1.1 ", "smooth_page_indicator: ^1.0.0+2", update them to the latest.
class MyItem {
String itemName;
String path;
MyItem(this.itemName, this.path);
}
class craouselImage extends StatefulWidget {
#override
_craouselImage createState() => _craouselImage();
}
class _craouselImage extends State<craouselImage> {
int activeIndex = 0;
List<MyItem> items = [
MyItem("item 1", 'assets/images/appiconlogo.png'),
MyItem("item 2", 'assets/images/Mockup4.png'),
];
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
CarouselSlider.builder(
itemCount: items.length,
options: CarouselOptions(
height: 400,
viewportFraction: 1,
autoPlay: true,
enlargeCenterPage: true,
enlargeStrategy: CenterPageEnlargeStrategy.height,
autoPlayInterval: const Duration(seconds: 1),
onPageChanged: (index, reason) {
setState(() {
activeIndex = index;
});
},
),
itemBuilder: (context, index, realIndex) {
final imgList = items[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(child: buildImage(imgList.path, index)),
const SizedBox(
height: 15,
),
buildText(imgList.itemName, index),
],
);
},
),
const SizedBox(
height: 22,
),
buildIndicator(),
const SizedBox(
height: 22,
),
//buildText(itemName, index),
// buildText(),
],
),
);
}
Widget buildImage(String imgList, int index) => Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
color: Colors.transparent,
child: Align(
alignment: Alignment.center,
child: Image.asset(
imgList,
fit: BoxFit.cover,
),
),
);
buildIndicator() => AnimatedSmoothIndicator(
activeIndex: activeIndex,
count: items.length,
effect: const JumpingDotEffect(
dotColor: Colors.black,
dotHeight: 15,
dotWidth: 15,
activeDotColor: mRed),
);
buildText(String itemName, int index) => Align(
alignment: FractionalOffset.bottomCenter,
child: Text(
itemName,
style: const TextStyle(
fontWeight: FontWeight.w700, fontSize: 23, color: mRed),
));
}

how to custom Tab Bar ? Flutter

I would like to customize my TabBar.
Right now I have this TabBar (stock UI) :
and I would like to have this UI :
I already coded the personalized tab but I don't know how to implement it.
There is my current code about my HomePage :
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new HomePageState();
}
}
class HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController =
TabController(length: 6, vsync: this); // initialise it here
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: [
Tab(text: "NewPay1.1"),
Tab(text: "NewMall 1.0"),
Tab(text: "海报"),
Tab(text: "企业版"),
Tab(text: "个版"),
Tab(text: "poa"),
],
),
title: Text('tabBar'),
),
body: TabBarView(
controller: _tabController,
children: [
// these are your pages
TaskListPage(),
TestPage(),
],
),
bottomNavigationBar: BottomAppBar(
color: Colors.white,
shape: CircularNotchedRectangle(),
child: Row(
children: <Widget>[
IconButton(
onPressed: () => _tabController.animateTo(0),
icon: Icon(Icons.home),
),
SizedBox(),
IconButton(
onPressed: () => _tabController.animateTo(1),
icon: Icon(Icons.more))
],
mainAxisAlignment: MainAxisAlignment.spaceAround,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
return TestPage().createState();
},
child: Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
),
);
}
}
then my CustomTab class :
class CustomTab extends StatefulWidget {
final Function(int) tabSelected;
final List<String> items;
const CustomTab({Key key, this.tabSelected, this.items}) : super(key: key);
#override
_CustomTabState createState() => _CustomTabState();
}
class _CustomTabState extends State<CustomTab> {
var categorySelected = 0;
#override
Widget build(BuildContext context) {
return _getListCategory();
}
Widget _getListCategory(){
ListView listCategory = new ListView.builder(
itemCount: widget.items.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index){
return _buildCategoryItem(index);
}
);
return new Container(
height: 50.0,
child: listCategory,
color: Colors.grey[200].withAlpha(200),
);
}
Widget _buildCategoryItem(index){
return new InkWell(
onTap: (){
setSelectedItem(index);
print("click");
},
child: new Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
margin: new EdgeInsets.only(left: 10.0),
child: new Material(
elevation: 2.0,
color: categorySelected == index ? Colors.black : Colors.grey,
borderRadius: const BorderRadius.all(const Radius.circular(25.0)),
child: new Container(
padding: new EdgeInsets.only(left: 12.0,top: 7.0,bottom: 7.0,right: 12.0),
child: new Text(widget.items[index],
style: new TextStyle(
color: categorySelected == index ? Colors.white : Colors.black),
),
),
),
)
],
),
);
}
void setSelectedItem(index) {
if(index != categorySelected) {
widget.tabSelected(index);
setState(() {
categorySelected = index;
});
}
}
}
There is already a plugin for this styled tabbar.
https://pub.dartlang.org/packages/bubble_tab_indicator
I hope this is what you are looking for :)
Yours Glup3
Try this code it is very simple
first initialize this
var radius = Radius.circular(150);
Then
class _Class_Name extends State<ClassName> {
var radius = Radius.circular(150);
#override
Widget build(BuildContext context) {
length: 2,
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
centerTitle: true,
leading: // if you need
),
title:
// if you need
// Scrollable rounded Tab bar
body: TabBar(
isScrollable: true,
labelColor: Colors.black,
tabs: <Widget>[
Tab(
text: "Tab 1",
),
Tab(
text: "Tab 2",
),
],
indicator: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(radius)),
color: HexColor('#005866')),
),
Container(
height: 80,
child: TabBarView(
children: <Widget>[
Container(
child: Center(
child: Text("Hello"),
),
),
Container(
child: Center(
child: Text("Hi"),
),
),
),
],
),
),
);
}

Flutter: hide and display app bar in scrolling detected

I'm having trouble with app bar animation, I'm using SilverAppBar, in my app. So, the problem is when I'm in the middle of my list and I scroll up, the app bar does not appear, but it appears just when scrolling reaches the top of the items list. I already tested the snap parameter and give it true, but not the result I expect. I have ideas about creating a custom animation for this, but I'm not too experienced in Flutter, and also if there is a way to add parameters, or another widget that will work for my situation, it would be great.
The actual code of the demo I'm using:
Widget _search() => Container(
color: Colors.grey[400],
child: SafeArea(
child: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
enabled: false,
style: TextStyle(fontSize: 16, color: Colors.white),
decoration: InputDecoration(
prefix: SizedBox(width: 12),
hintText: "Search",
contentPadding:
EdgeInsets.symmetric(horizontal: 32.0, vertical: 14.0),
border: InputBorder.none,
),
),
),
)),
);
Container _buildBody() {
return Container(
child: new GridView.count(
crossAxisCount: 2,
children: List.generate(100, (index) {
return Center(
child: Text(
'Item $index',
style: Theme.of(context).textTheme.headline,
),
);
}),
));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: false,
body: new NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
title: Text("Demo",
style: TextStyle(
color: Colors.white,
)),
pinned: false,
floating: true,
forceElevated: innerBoxIsScrolled,
),
];
},
body: new Column(children: <Widget>[
_search(),
new Expanded(child: _buildBody())
])));
}
The result I have now:
Image 1
The result I got after giving true to the snap parameter:
Image 2
Plenty of applications like WhatsApp, Facebook, LinkedIn ... have this animating app bar. To explain more what exactly I expect with this animating app bar, I added an example of Google Play Store, showing the wanted animation: Play Store example
I had a similar issue with CustomScrollView and SliverAppbar using refresh indicator i ended up creating my own custom appbar.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class HomeView extends StatefulWidget {
#override
HomeState createState() => HomeState();
}
class HomeState extends State<HomeView> with SingleTickerProviderStateMixin {
bool _isAppbar = true;
ScrollController _scrollController = new ScrollController();
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
appBarStatus(false);
}
if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
appBarStatus(true);
}
});
}
void appBarStatus(bool status) {
setState(() {
_isAppbar = status;
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: AnimatedContainer(
height: _isAppbar ? 55.0 : 0.0,
duration: Duration(milliseconds: 200),
child: CustomAppBar(),
),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return container();
},
),
),
);
}
}
Widget container() {
return Container(
height: 80.0,
color: Colors.pink,
margin: EdgeInsets.all(8.0),
width: 100,
child: Center(
child: Text(
'Container',
style: TextStyle(
fontSize: 18.0,
),
)),
);
}
class CustomAppBar extends StatefulWidget {
#override
AppBarView createState() => new AppBarView();
}
class AppBarView extends State<CustomAppBar> {
#override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: Colors.white,
leading: InkWell(
onTap: () => {},
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: CircleAvatar(
backgroundColor: Colors.white,
child: ClipOval(
child: Image.network(
'https://images.squarespace-cdn.com/content/5aee389b3c3a531e6245ae76/1530965251082-9L40PL9QH6PATNQ93LUK/linkedinPortraits_DwayneBrown08.jpg?format=1000w&content-type=image%2Fjpeg'),
),
),
),
),
actions: <Widget>[
IconButton(
alignment: Alignment.centerLeft,
icon: Icon(
Icons.search,
color: Colors.black,
),
onPressed: () {},
),
],
title: Container(
alignment: Alignment.centerLeft,
child: Text("Custom Appbar", style: TextStyle(color: Colors.black),)
),
);
}
}
To get this functionality to work, you will need to use the CustomScrollView widget instead of NestedScrollView. Google Documentation
Here is a working example:
class MyHomeState extends State<MyHome> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: false,
snap: false,
floating: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 50,
),
),
],
)
);
}
}
Example of this running here
I was able to make the floating Appbar with Tabbar similar to that of WhatsApp by using SliverAppbar with NestedScrollView. Do remember to add floatHeaderSlivers: true, in NestedScrollView. Link to sample code
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: CustomSliverAppbar(),
);
}
}
class CustomSliverAppbar extends StatefulWidget {
#override
_CustomSliverAppbarState createState() => _CustomSliverAppbarState();
}
class _CustomSliverAppbarState extends State<CustomSliverAppbar>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(
initialIndex: 0,
length: 2,
vsync: this,
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text(
"WhatsApp type sliver appbar",
),
centerTitle: true,
pinned: true,
floating: true,
bottom: TabBar(
indicatorColor: Colors.black,
labelPadding: const EdgeInsets.only(
bottom: 16,
),
controller: _tabController,
tabs: [
Text("TAB A"),
Text("TAB B"),
]),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
TabA(),
const Center(
child: Text('Display Tab 2',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
),
],
),
),
);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
}
class TabA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scrollbar(
child: ListView.separated(
separatorBuilder: (context, child) => Divider(
height: 1,
),
padding: EdgeInsets.all(0.0),
itemCount: 30,
itemBuilder: (context, i) {
return Container(
height: 100,
width: double.infinity,
color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
);
},
),
);
}
}

Flutter: Notifications UI

I can't distinguish new Firebase Cloud Messaging notifications from older ones in my Flutter UI. How can I fix this? Thanks.
EXAMPLE: 3 users comment on an author's post that already has 2 "seen" comments. The author's post gets a +3 red marble on it. When the author clicks on this post in the Notifications tab, the user is routed to its comments, that +3 reduces to 0, and the bottom tab marble reduces to 3 (6-3=3) as well.
Dummy UI Code and Pics below.
1. A blank "home page;"
2. a dummy notifications page with posts that have been replied to
3. On clicking a post, a comments page.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
routes: <String, WidgetBuilder>{
"/comment": (BuildContext context) => new commentFoundation(),
},
home: MainScreen());
}
}
//The content of the notification tab.
class Notification extends StatefulWidget {
#override
_NotificationState createState() => new _NotificationState();
}
class _NotificationState extends State<Notification> {
#override
Widget build(BuildContext context) {
return _buildPostList();
}
Widget _buildPostList() {
return new Scaffold(
backgroundColor: Colors.blue,
body: new Scrollbar(
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Stack(children: <Widget>[
new Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 0.0, left: 12.0),
child: DummyPost()),
],
),
replyNotification(),
]));
},
itemCount: 2,
)));
}
Widget replyNotification() {
return new Positioned(
left: 175,
top: 205.5,
child: new Container(
padding: EdgeInsets.only(left: 3.0, right: 3.0),
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(30),
),
constraints: BoxConstraints(
minWidth: 30,
minHeight: 30,
),
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 0.0),
child: new Text(
'+',
style: new TextStyle(
color: Colors.white,
fontSize: 26,
),
textAlign: TextAlign.center,
)),
Container(
padding: EdgeInsets.only(right: 0.0),
child: new Text(
'3',
style: new TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
)),
],
)),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Center(
child: new Container(
child: Text("HomePageStuff"),
));
}
}
//A dummy post in notififications tab
class DummyPost extends StatefulWidget {
#override
_DummyPostState createState() => new _DummyPostState();
}
class _DummyPostState extends State<DummyPost> {
bool monVal = false;
bool reportPressed = false;
Widget commentButton() {
return RaisedButton(
child: Text("ReplyButton"),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => commentFoundation())),
);
}
#override
Widget build(BuildContext context) {
return postList();
}
Widget postList() {
return Column(children: <Widget>[
new GestureDetector(
//this type works
onTap: () => Navigator.pushNamed(context, '/comment'),
child: new Stack(children: <Widget>[
Column(children: <Widget>[
SizedBox(
height: 10.0,
),
Container(
margin: EdgeInsets.only(top: 35.0),
width: 390.0,
child: Card(
shape: RoundedRectangleBorder(
//15?
borderRadius: BorderRadius.circular(5.0),
),
color: Colors.white,
elevation: 2.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Flexible(
child: ListTile(
// The main post content
subtitle: Container(
height: 130.0,
margin: EdgeInsets.only(
top: 15.0, left: 15.0, right: 15.0),
child: Container(
child: Center(
child: Text(
"This is a dummy post",
textAlign: TextAlign.center,
style: TextStyle(
wordSpacing: 3.0,
color: Colors.black,
fontSize: 18.0),
))),
),
)),
//button row
new Container(
width: 400.0,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 0.0),
width: 190.0,
height: 40.0,
child: commentButton(),
),
// Text to indicate comment numbers
],
)),
],
))),
]),
]))
]);
}
}
//the entire comment page.
class commentFoundation extends StatefulWidget {
#override
_commentFoundationState createState() => new _commentFoundationState();
}
class _commentFoundationState extends State<commentFoundation> {
ScrollController _scrollController = new ScrollController();
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.green,
appBar: _buildAppBar(),
body: new Stack(children: <Widget>[
ListView.builder(
shrinkWrap: true,
controller: _scrollController,
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return new individualComment();
}),
]));
}
Widget _buildAppBar() {
return AppBar(
title: Text("REPLIES"),
automaticallyImplyLeading: true,
backgroundColor: Colors.white,
elevation: 0.0,
);
}
}
//Each individual comment you see when you click on a post
class individualComment extends StatefulWidget {
final String title;
const individualComment({Key key, this.title}) : super(key: key);
#override
_individualCommentState createState() => new _individualCommentState();
}
class _individualCommentState extends State<individualComment> {
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
Column(children: <Widget>[
//this used to be an expanded
new Container(
child: Card(
elevation: 0.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ListTile(
// The avatar
leading: Container(
child: Icon(
Icons.accessibility_new,
)),
// Title, location, and time
// The main post content
subtitle: Container(
margin: EdgeInsets.only(top: 10.0, bottom: 0.0),
child: Text(
"This is a reply",
style: TextStyle(
color: Colors.black,
fontSize: .0364609 *
MediaQuery.of(context).size.width),
),
),
),
],
))),
])
],
);
}
}
//*****This is just the tabbar.
class MainScreen extends StatefulWidget {
#override
_MainScreenstate createState() => new _MainScreenstate();
}
class _MainScreenstate extends State<MainScreen> with TickerProviderStateMixin {
String _lastSelected = '0';
final List<Widget> _bodyOptions = [
HomePage(),
Notification(),
];
int _selectedIndex = 0;
bool tab = true;
_selectedTab(int index) {
setState(() {
_lastSelected = '$index';
});
}
Widget __buildTabs() {
return Scaffold(
body: _bodyOptions.elementAt(int.parse(_lastSelected)),
//around the FAB
backgroundColor: const Color(0xFFF4F4F4),
bottomNavigationBar: new Stack(
children: <Widget>[
FABBottomAppBar(
height: 45.0,
centerItemText: '',
color: Colors.black,
selectedColor: Colors.blue,
notchedShape: CircularNotchedRectangle(),
onTabSelected: _selectedTab,
items: [
FABBottomAppBarItem(iconData: Icons.search, text: 'Home'),
FABBottomAppBarItem(
iconData: Icons.notifications, text: 'Notifications'),
],
),
//the red "notification" marble
new Positioned(
right: 20,
top: 0.5,
child: new Container(
padding: EdgeInsets.all(1),
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(12),
),
constraints: BoxConstraints(
minWidth: 20,
minHeight: 20,
),
child: new Text(
'6',
style: new TextStyle(
color: Colors.white,
fontSize: 14,
),
textAlign: TextAlign.center,
),
),
)
],
));
}
#override
Widget build(BuildContext context) {
return __buildTabs();
}
}
//Everything below is boiler plate tabbar code.
class FABBottomAppBarItem {
FABBottomAppBarItem({this.iconData, this.text});
IconData iconData;
String text;
}
class FABBottomAppBar extends StatefulWidget {
FABBottomAppBar({
this.items,
this.centerItemText,
this.height: 60.0,
this.iconSize: 24.0,
this.backgroundColor,
this.color,
this.selectedColor,
this.notchedShape,
this.onTabSelected,
}) {
assert(this.items.length == 2 || this.items.length == 4);
}
final List<FABBottomAppBarItem> items;
final String centerItemText;
final double height;
final double iconSize;
final Color backgroundColor;
final Color color;
final Color selectedColor;
final NotchedShape notchedShape;
final ValueChanged<int> onTabSelected;
#override
State<StatefulWidget> createState() => FABBottomAppBarState();
}
class FABBottomAppBarState extends State<FABBottomAppBar> {
int _selectedIndex = 0;
_updateIndex(int index) {
widget.onTabSelected(index);
setState(() {
_selectedIndex = index;
});
return _selectedIndex;
}
#override
Widget build(BuildContext context) {
List<Widget> items = List.generate(widget.items.length, (int index) {
return _buildTabItem(
item: widget.items[index],
index: index,
onPressed: _updateIndex,
);
});
items.insert(items.length >> 1, _buildMiddleTabItem());
return BottomAppBar(
elevation: 0.0,
notchMargin: 2.0,
shape: CircularNotchedRectangle(),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items,
),
//color of bottom
color: Colors.white,
);
}
Widget _buildMiddleTabItem() {
return Expanded(
child: SizedBox(
height: widget.height,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: widget.iconSize),
Text(
widget.centerItemText ?? '',
style: TextStyle(color: widget.color),
),
],
),
),
);
}
Widget _buildTabItem({
FABBottomAppBarItem item,
int index,
ValueChanged<int> onPressed,
}) {
Color color = _selectedIndex == index ? widget.selectedColor : widget.color;
return Expanded(
child: SizedBox(
height: widget.height,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => onPressed(index),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(item.iconData, color: color, size: widget.iconSize),
Text(
item.text,
style: TextStyle(color: color),
)
],
),
),
),
),
);
}
}
This seems to be a broad question and I'm unsure if you're asking for suggestions on how you can implement tracking read and unread notifications, or troubleshooting help on your existing code. I suggest checking tips posted on this thread on how you can manage notification counters.

Resources