PageView Builder; Text is repeated through page view - dart

I'm building some pageview with a lot of text and want to scroll horizontally if the content exceed the screen
This is for reading, like a book. I've tried with the Text instead of RichText but got the same results.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(245, 205, 121, 1.0 ),
title: Text('MyTitle'),
),
body:
new PageView.builder(
physics: ScrollPhysics(),
controller: controller,
itemBuilder: (context, index) {
return new Center(
child: new RichText(
text: TextSpan(style: TextStyle(fontSize: 25.0, color: Colors.black), text: myText),
textAlign: TextAlign.start,
textScaleFactor: 1.0,
textDirection: TextDirection.ltr,
),
);
},
)
)
);
}
var myText = "A Lot of text here"
I expect to have the continuation of lorem ipsum but I have the same content in all pages.

That function will help you, I guess try this out.
String currentPageText(int index, String myText) {
// I assume 1000 is the length of the text that fits into your page, you can change it according to your page&textFont&textStyle
if (index * 1000 < myText.length) {
// you dont need the next line code if your index start from 1 but it can stay for a safety
if (index == 0) return myText.substring(0, index * 1000);
return myText.substring((index - 1) * 1000, index * 1000);
} else {
if ((index - 1) * 1000 > myText.length) {
return "Your Text Done"; // This is for your text finishes but your index still increases
}
return myText.substring((index - 1) * 1000, myText.length);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Color.fromRGBO(245, 205, 121, 1.0),
title: Text('MyTitle'),
),
body:
new PageView.builder(
physics: ScrollPhysics(),
controller: controller,
itemBuilder: (context, index) {
return new Center(
child: new RichText(
text: TextSpan(
style: TextStyle(fontSize: 25.0, color: Colors.black),
text: currentPageText(index, myText)),
textAlign: TextAlign.start,
textScaleFactor: 1.0,
textDirection: TextDirection.ltr,
),
);
},
)
)
);
}
example to see in dartpad

Related

Flutter : Refresh same screen with different data and back button

I have recently started exploring flutter few days back. I have created a list which has some rows.
Some rows has the Child data.
Right now screen has customised button on the top.
final topAppBar = AppBar(
elevation: 0.1,
backgroundColor: Color.fromRGBO(0, 113, 188, 1.0),
title: Text("RESOURCES", style: TextStyle(
color: Colors.white,
fontFamily: 'Raleway-ExtraBold',
fontWeight: FontWeight.w900,
fontSize: 20.0,
),),
leading: IconButton(
icon: new Image.asset('assets/images/settings.png'),
),
);
When user clicks on those rows I want to just refresh the list with child data and push effect with updating “back button” on the top.
The below code is able to navigate the screen with push effect but how can we maintain the state of application with data as well as back button.
ListTile makeResourcesListTile(Resources resources) => ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 0.0),
title: Text(
resources.title,
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
fontWeight: FontWeight.bold,
fontFamily: "Raleway-Bold",
),
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0),
onTap: () {
Navigator.pushNamed(context, ‘/listScreen’);
},
);
Please suggest. Thank you in advance
I think you should have a look at: Passing data between screens in Flutter
Is this what you are looking for?
LE:
If you just want to change data source for the list and add a back button, please try this code:
class MyHomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
bool showDetails = false;
String title = 'Resources';
List<Resource> resources = [
new Resource('1', 'one', null),
new Resource('2', 'two', [new Resource('Child', 'Child', null)]),
new Resource('3', 'three', null),
new Resource('4', 'four', [
new Resource('Child', 'Child', null),
new Resource('Child', 'Child', null)
]),
new Resource('5', 'five', null)
];
List<Resource> currentSource;
#override
Widget build(BuildContext context) {
if (!showDetails) {
currentSource = resources;
}
Widget showResourcesList() {
return new ListView.builder(
itemCount: currentSource.length,
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: Center(
child: Text(currentSource[index].name),
),
onTap: () {
setState(() {
if (currentSource[index].children != null) {
title = 'Children for ' + currentSource[index].name;
currentSource = resources[index].children;
showDetails = true;
}
});
});
});
}
Widget showBackButton() {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
setState(() {
showDetails = false;
currentSource = resources;
title = 'Resources';
});
},
);
}
Widget showSettingsButton() {
return IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
);
}
return Scaffold(
appBar: AppBar(
elevation: 0.1,
backgroundColor: Color.fromRGBO(0, 113, 188, 1.0),
title: Text(
title,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 20.0,
),
),
leading: showDetails ? showBackButton() : showSettingsButton(),
),
body: showResourcesList(),
);
}
}
class Resource {
String name;
String description;
List<Resource> children;
Resource(this.name, this.description, this.children);
}
I used a bool variable (showDetails) which represents the app state and I change the data source when tapping on a listTile.

Is there a way to convert Future<List<String>> to List<dynamic>?

Edit:
Home Page - I'm fetching a list of strings from my firebase collection. I then want to make a call to firestore storage and get the downloadable image link and store it in a list that I will pass to Page 2. The code below is how i'm getting the downloadable links.
Future<List<String>> test(List images) async{
List<String> listOfImages = List<String>();
for(int i = 0; i < images.length; i++){
final ref = FirebaseStorage.instance.ref().child(images[i]);
var url = await ref.getDownloadURL();
listOfImages.add(url);
}
return listOfImages;
}
I'm then passing the list of Images in the following manner
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CarDetailScreen(
carImages: test( car['images'])
)),
);
Page 2 - I'm receiving a Future> that I would like to convert to a List
List<T> map<T>(List list, Function handler) {
List<T> result = [];
for (var i = 0; i < list.length; i++) {
result.add(handler(i, list[i]));
}
return result;
}
new CarouselSlider(
items: ***["This is where I need a List"]***.map((url) {
return new Container(
margin: new EdgeInsets.all(5.0),
child:
new GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ViewCarImages(
carImages: _getImages(snapshot))),
);
},
child:
new ClipRRect(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
child:
new Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
)
)
)
);
}).toList(),
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: false,
)
You can just return a List of Widget, like this:
Future<List<Widget>> test(List images) async{
List<Widget> listOfImages = List<Widget>();
for(int i = 0; i < images.length; i++){
final ref = FirebaseStorage.instance.ref().child(images[i]);
var url = await ref.getDownloadURL();
listOfImages.add(Image.network(url));
}
return listOfImages;
}
Below code will solve your problem. In carouselSlide class(check items variable) written below and you need to follow below procedure to work the Future object in carousel
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: Colors.white,
title: Text("some text",
style: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.black,
fontSize: 16.0)),
centerTitle: false,
actions: <Widget>[
IconButton(
tooltip: 'Search something',
icon: Icon(
Icons.search,
color: SharedColor().sapphireDarkBlue,
),
onPressed: () async {
var selected = await showSearch<int>(
context: context, delegate: _delegate);
if (selected != null && selected != _lastIntegerSelected) {
setState(() {
//pass the data from searchfield query to here
});
}
},
),
],
automaticallyImplyLeading: false,
),
body: _carouselBuild(),
);
}
FutureBuilder<caraouselImage> _carouselBuild() {
return FutureBuilder<caraouselImage>(
future: ImageRestApiManager().caraouselImage(myEmail),
builder: (context, AsyncSnapshot<caraouselImage> snapshot) {
if (snapshot.hasData) {
return CarouselSlider(
items: snapshot.data.collection.map((i) {
return Builder(builder: (BuildContext context) {
return Container(
margin: EdgeInsets.all(5.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Stack(children: <Widget>[
Image.network(i.imageurl,
fit: BoxFit.cover, width: 1000.0),
Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(200, 0, 0, 0),
Color.fromARGB(0, 0, 0, 0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
padding: EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
_currentCookieSelected.toString(),
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
),
]),
),
);
});
}).toList(),
autoPlay: false,
enlargeCenterPage: true,
viewportFraction: 0.9,
aspectRatio: 2.0,
onPageChanged: (index) {
setState(() {
_currentImageSelected = snapshot.data.collection[index].id;
});
});
} else {
return CircularProgressIndicator();
}
});
}
}
I solved using below youtube tutorial
https://www.youtube.com/watch?v=xBLtPDHvT44
A FutureBuilder should get you wht you're looking for. Here, I've wrapped your CarouselSlider in a FutureBuilder, and have the future property set to your carImages Future. When your list of Strings is ready, the FutureBuilder will create the CarouselSlider via its builder method, and all of the list items will be present then:
return FutureBuilder<List<String>>(
future: carImages,
initialData: [],
builder: (context, snapshot) {
return new CarouselSlider(
viewportFraction: 0.9,
aspectRatio: 2.0,
autoPlay: false,
items: snapshot.data.map((url) {
return new Container(
margin: new EdgeInsets.all(5.0),
child:
new GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ViewCarImages(
carImages: _getImages(snapshot))),
);
},
child: new ClipRRect(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
child: new Image.network(
url,
fit: BoxFit.cover,
width: 1000.0,
)
)
)
);
}).toList(),
);
}
);

Horizontally scrollable cards with Snap effect in flutter

I want to create a list of cards scrolling horizontally with snap to fit effect when swiped either from left or right.
Each card has some spacing between them and fit to screen similar to below image
Apart from that these horizontally scrollable list elements should be contained inside a vertically scrollable list.
I all I am able to achieve is only displaying a list of horizontal scrolling cards after following example in flutter docs.
class SnapCarousel extends StatelessWidget {
#override
Widget build(BuildContext context) {
final title = 'Horizontal List';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 200.0,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 160.0,
color: Colors.red,
),
Container(
width: 160.0,
color: Colors.blue,
),
Container(
width: 160.0,
color: Colors.green,
),
Container(
width: 160.0,
color: Colors.yellow,
),
Container(
width: 160.0,
color: Colors.orange,
),
],
),
),
),
);
}
}
Use PageView and ListView:
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: MyHomePage()));
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carousel in vertical scrollable'),
),
body: ListView.builder(
padding: EdgeInsets.symmetric(vertical: 16.0),
itemBuilder: (BuildContext context, int index) {
if(index % 2 == 0) {
return _buildCarousel(context, index ~/ 2);
}
else {
return Divider();
}
},
),
);
}
Widget _buildCarousel(BuildContext context, int carouselIndex) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Carousel $carouselIndex'),
SizedBox(
// you may want to use an aspect ratio here for tablet support
height: 200.0,
child: PageView.builder(
// store this controller in a State to save the carousel scroll position
controller: PageController(viewportFraction: 0.8),
itemBuilder: (BuildContext context, int itemIndex) {
return _buildCarouselItem(context, carouselIndex, itemIndex);
},
),
)
],
);
}
Widget _buildCarouselItem(BuildContext context, int carouselIndex, int itemIndex) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
);
}
}
Screenshot:
If you don't want to use any 3rd party packages, you can simply try this:
class _HomePageState extends State<HomePage> {
int _index = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i + 1}",
style: TextStyle(fontSize: 32),
),
),
),
);
},
),
),
),
);
}
}
this is an old question, and I arrived here looking for something else ;-), but what WitVault was lookig is done easy with this package: https://pub.dev/packages/flutter_swiper
The implementation:
Put the dependencies in pubsec.yaml:
dependencies:
flutter_swiper: ^1.1.6
Import it in the page where you need it:
import 'package:flutter_swiper/flutter_swiper.dart';
In the layout:
new Swiper(
itemBuilder: (BuildContext context, int index) {
return new Image.network(
"http://via.placeholder.com/288x188",
fit: BoxFit.fill,
);
},
itemCount: 10,
viewportFraction: 0.8,
scale: 0.9,
)
To achieve the snap effect via ListView, just set the physics to PageScrollPhysics
const List<Widget> children = [
ContainerCard(),
ContainerCard(),
ContainerCard(),
];
ListView.builder(
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(), // this for snapping
itemCount: children.length,
itemBuilder: (_, index) => children[index],
)
Advanced Snap List
If you are looking for advanced usages, such as dynamic item sizes, configurable snap points, visualization of items, and essential control (such as scrollToIndex, animate) you should use the native-based SnappyListView with way more features.
SnappyListView(
itemCount: Colors.accents.length,
itemBuilder: (context, index) {
return Container(
height: 100,
color: Colors.accents.elementAt(index),
child: Text("Index: $index"),
),
);
I believe the answer solution from CopsOnRoad is better and simple for someone who don't want to use a 3rd party library. However, since there is no animation, I add the scale animation when the card is viewed (expand) and the previous card is swiped (shrink) using index. So what happened is whenever the first time the page load, 1st and 2nd card won't have any animation, and when the card is swiped, only the previous and current card have the scale animation. So this is my implementation:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentIndex = -1, previousIndex = 0;
double getAnimationValue(int currentIndex, int widgetIndex, int previousIndex,
{bool begin = true}) {
if (widgetIndex == currentIndex) {
return begin ? 0.9 : 1;
} else {
return begin ? 1 : 0.9;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 200, // card height
child: PageView.builder(
itemCount: 10,
controller: PageController(viewportFraction: 0.7),
onPageChanged: (int index) {
setState(() {
if (currentIndex != -1) {
previousIndex = currentIndex;
}
currentIndex = index;
});
},
itemBuilder: (_, widgetIndex) {
return (currentIndex != -1 &&
(previousIndex == widgetIndex ||
widgetIndex == currentIndex))
? TweenAnimationBuilder(
duration: const Duration(milliseconds: 400),
tween: Tween<double>(
begin: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
),
end: getAnimationValue(
currentIndex,
widgetIndex,
previousIndex,
begin: false,
),
),
builder: (context, value, child) {
return Transform.scale(
scale: value,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Current Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
)
: Transform.scale(
// this is used when you want to disable animation when initialized the page
scale:
(widgetIndex == 0 && currentIndex == -1) ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Card${widgetIndex + 1}",
style: const TextStyle(fontSize: 30),
),
Text(
"$widgetIndex >> Widget Index << $widgetIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$currentIndex >> Init Index << $currentIndex",
style: const TextStyle(fontSize: 22),
),
Text(
"$previousIndex >> Previous Index << $previousIndex",
style: const TextStyle(fontSize: 22),
),
],
),
),
);
},
),
),
],
),
);
}
}
I used TweenAnimationBuilder for this animation and hardcoded the widget. You can use method for your widget or use package flutter_animate for easy animation whenever necessary.

Flutter: Change text when FlexibleSpaceBar is collapsed

I have looked through the Flutter documentation to try and find an event, callback or even a state that I could hook into when the FlexibleSpaceBar is collapsed or expanded.
return new FlexibleSpaceBar(
title: new Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Text(_name, style: textTheme.headline),
new Text(_caption, style: textTheme.caption)
]),
centerTitle: false,
background: getImage());`
When the FlexibleSpaceBar is snapped in (collapsed), I want to hide the _caption text and only display the _name text. When it is expanded fully, I obviously want to display both _name & _caption.
How do I go about doing that?
Im new to flutter, so I am somewhat lost on this.
Also reported at https://github.com/flutter/flutter/issues/18567
It's not hard to create your own FlexibleSpaceBar.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController controller = ScrollController();
#override
Widget build(BuildContext context) {
return CustomScrollView(
physics: ClampingScrollPhysics(),
controller: controller,
slivers: [
SliverAppBar(
expandedHeight: 220.0,
floating: true,
pinned: true,
elevation: 50,
backgroundColor: Colors.pink,
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
flexibleSpace: _MyAppSpace(),
),
SliverList(
delegate: SliverChildListDelegate(
List.generate(
200,
(index) => Card(
child: Padding(
padding: EdgeInsets.all(10),
child: Text('text $index'),
),
),
),
),
)
],
);
}
}
class _MyAppSpace extends StatelessWidget {
const _MyAppSpace({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, c) {
final settings = context
.dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
final deltaExtent = settings.maxExtent - settings.minExtent;
final t =
(1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent)
.clamp(0.0, 1.0) as double;
final fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
const fadeEnd = 1.0;
final opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);
return Stack(
children: [
Center(
child: Opacity(
opacity: 1 - opacity,
child: getTitle(
'Collapsed Title',
)),
),
Opacity(
opacity: opacity,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
getImage(),
getTitle(
'Expended Title',
)
],
),
),
],
);
},
);
}
Widget getImage() {
return Container(
width: double.infinity,
child: Image.network(
'https://source.unsplash.com/daily?code',
fit: BoxFit.cover,
),
);
}
Widget getTitle(String text) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 26.0,
fontWeight: FontWeight.bold,
),
),
);
}
}
You can use AnimatedOpacity class.
flexibleSpace: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
var top = constraints.biggest.height;
return FlexibleSpaceBar(
title: AnimatedOpacity(
duration: Duration(milliseconds: 300),
//opacity: top > 71 && top < 91 ? 1.0 : 0.0,
child: Text(
top > 71 && top < 91 ? "Collapse" : "Expanded",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
)),
background: Image.network(
"https://images.ctfassets.net/pjshm78m9jt4/383122_header/d79a41045d07d114941f7641c83eea6d/importedImage383122_header",
fit: BoxFit.cover,
));
}),
Can check original answer from this link
https://stackoverflow.com/a/53380630/9719695
It can be done like this :
inside your initState method add the scroll listener like that :
ScrollController _controller;
bool silverCollapsed = false;
String myTitle = "default title";
#override
void initState() {
super.initState();
_controller = ScrollController();
_controller.addListener(() {
if (_controller.offset > 220 && !_controller.position.outOfRange) {
if(!silverCollapsed){
// do what ever you want when silver is collapsing !
myTitle = "silver collapsed !";
silverCollapsed = true;
setState(() {});
}
}
if (_controller.offset <= 220 && !_controller.position.outOfRange) {
if(silverCollapsed){
// do what ever you want when silver is expanding !
myTitle = "silver expanded !";
silverCollapsed = false;
setState(() {});
}
}
});
}
then wrap your silverAppBar inside CustomScrollView and add the controller to this CustomScrollView like that :
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
controller: _controller,
slivers: <Widget>[
SliverAppBar(
expandedHeight: 300,
title: myTitle,
flexibleSpace: FlexibleSpaceBar(),
),
SliverList(
delegate: SliverChildListDelegate(<Widget>[
// your widgets inside here !
]),
),
],
),
);
}
finally change the condition value _controller.offset > 220 to fit your need !
FlexibleSpaceBar per se won't be enough. You need to wrap it into CustomScrollView and SliverAppBar. These widgets must be controller by a ScrollController, which will fire an event whenever scroll offset changes. Based on it, you can find out if app bar is collapsed or expanded, and change the content accordingly. Here you will find a working example.
Give an height in padding in FlexibleSpaceBar
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.only(
top: 100, // give the value
title: Text(
"Test"
),
Follow up to Vishnu Suresh answer:
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.only(
top: kToolbarHeight, // give the value
title: Text(
"Test"
),
This will use the appbar height for the padding.

Flutter - How to set status bar color when AppBar not present

How to set status bar color when AppBar not present.
I have tried this but not working.
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
return new Scaffold(
body: new Container(
color: UniQueryColors.colorBackground,
child: new ListView.builder(
itemCount: 7,
itemBuilder: (BuildContext context, int index){
if (index == 0){
return addTopInfoSection();
}
},
),
),
);
}
Output look like this:
First, import services package:
import 'package:flutter/services.dart';
Next, simply put this in the build function of your App:
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.blue, //or set color with: Color(0xFF0000FF)
));
Additionally, you can set useful properties like: statusBarIconBrightness, systemNavigationBarColor or systemNavigationBarDividerColor
If you prefer a more flutter/widget way of doing the same thing, consider using the AnnotatedRegion<SystemUiOverlayStyle> widget.
The value: property can be set to a SystemUiOverlayStyle() object containing the same properties as shown above.
For more infos, head over to the API Docs
If you take a look at the source code of AppBar, you can see they use an AnnotatedRegion widget. AnnotedRegion widget gives you more control on System overlay style. This is a more fluttery way to configure the system styles when an app bar is not used.
From my understanding, this widget automatically sets the statusbar/navigationbar color when the widget wrapped in it gets overlaid by the statusbar/navigationbar.
You can wrap your widget like this:
import 'package:flutter/services.dart';
...
Widget build(BuildContext context) {
return Scaffold(
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: ...,
),
);
}
For more information about AnnotatedRegion widget head over to the API Docs
As the solution is already mentioned, I am implementing it in a different approach. The approach followed is removing AppBar and changing the color of the status bar using Container widget.
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Test',
home: Scaffold(
primary: true,
appBar: EmptyAppBar(),
body: MyScaffold(),
),
),
);
}
class MyScaffold extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Text(
'Test',
),
);
}
}
class EmptyAppBar extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
);
}
#override
Size get preferredSize => Size(0.0, 0.0);
}
Here I am using EmptyAppBar class for removing the AppBar which is by default present in Scaffold
In EmptyAppBar class we can choose the required color in the container widget.
After that, you have your own custom MyScaffold class for creating your widgets. In my code, I've created a text.
Reference: GitHub Issue
On Android, add the following to onCreate in MainActivity.java, after the call to super.onCreate(savedInstanceState);
getWindow().setStatusBarColor(0x00000000);
or you can use the the flutter_statusbarcolor plugin
changeStatusColor(Color color) async {
try {
await FlutterStatusbarcolor.setStatusBarColor(color);
} on PlatformException catch (e) {
print(e);
}
}
Sample project
While searching for SystemChrome I found this: https://docs.flutter.io/flutter/services/SystemChrome/setSystemUIOverlayStyle.html
Right above the sample code is a paragraph about AppBar.brightness.
You should should be able to add an AppBar like this:
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
return new Scaffold(
appBar: new AppBar(
title: new Text('Nice title!'),
brightness: Brightness.light,
),
body: new Container(
color: UniQueryColors.colorBackground,
child: new ListView.builder(
itemCount: 7,
itemBuilder: (BuildContext context, int index){
if (index == 0){
return addTopInfoSection();
}
},
),
),
);
}
Heres info about the Brightness
you should solve this question in two ways:
you did not set appBar then you just write in this way:
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.black,
));
or
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(
statusBarColor: Colors.white,
));
you always set appBar so you should set appBar but not upon code:
Scaffold(
appBar: AppBar(
brightness: Brightness.light,
)
)
or
Scaffold(
appBar: AppBar(
brightness: Brightness.dark,
)
)
I tried many methods but they didn’t work. And i found another method, use safeArea ,AnnotatedRegion and Scaffold
AnnotatedRegion(
// status icon and text color, dark:black light:white
value: SystemUiOverlayStyle.dark,
child: Scaffold(
// statusbar color
backgroundColor: Colors.white,
body : SafeArea(****)
)
}
The code implements the white status bar and black text
The status bar colour is rendered by the Android system. Whether that can be set from Flutter or not is up for debate: How to make Android status bar light in Flutter
What you can do however, is change the status bar colour in the Android specific code by editing the theme: How to change the status bar color in android
For iOS you'll have to see their documentation - I'm not familiar with the platform.
There are in fact two Dart libraries, one for setting the light/dark theme of the statusbar and the other for setting the colour. I haven't used either, but clearly someone else has had the same issue you're facing and ended up developing their own package.
If you dont want to use app bar, then
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white, # status bar color
toolbarHeight: 0,
brightness: Brightness.light # or Brightness.dark
Use
AppBar(
systemOverlayStyle: SystemUiOverlayStyle(statusBarColor: Colors.orange),
)
or
AppBar(
backgroundColor: Colors.red, // Status bar color
brightness: Brightness.light, // Status bar brightness
)
Widget build(BuildContext context) {
return new Scaffold(
body: new Container(
color: UniQueryColors.colorBackground,
/* Wrapping ListView.builder with MediaQuery.removePadding() removes the default padding of the ListView.builder() and the status bar takes the color of the app background */
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: 7,
itemBuilder: (BuildContext context, int index){
if (index == 0){
return addTopInfoSection();
}
},
),
),
),
);
}
#override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Colors.green, // Color of you choice
statusBarIconBrightness: Brightness.light,
),
child: Scaffold(
body: SafeArea(child: Text("Center Text")),
),
);
}
Use EmptyAppBar, with some code for restoring color as in AppBar.
class EmptyAppBar extends StatelessWidget implements PreferredSizeWidget {
static const double _defaultElevation = 4.0;
#override
Widget build(BuildContext context) {
final ThemeData themeData = Theme.of(context);
final AppBarTheme appBarTheme = AppBarTheme.of(context);
final Brightness brightness = appBarTheme.brightness
?? themeData.primaryColorBrightness;
final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark;
return Semantics(
container: true,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: Material(
color: appBarTheme.color
?? themeData.primaryColor,
elevation: appBarTheme.elevation
?? _defaultElevation,
child: Semantics(
explicitChildNodes: true,
child: Container(),
),
),
),
);
}
#override
Size get preferredSize => Size(0.0,0.0);
}
Use the following in your main function to change the status bar color for all the views/screens. This will work even without an app bar.
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.dark, // this will change the brightness of the icons
statusBarColor: Colors.white, // or any color you want
));
this best status bar like default material design theme without AppBar()
Container(width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).padding.top, color: Theme.of(context).accentColor)
If you are implementing dark and light theme, it can be tricky and you don't want the app bar, here is a work around i implemented
appBar: AppBar(
toolbarHeight: 0.0,
elevation: 0.0,
),
Implement an empty appBar, and on your ThemeData implement like below, remember to state AppBar background color
theme: ThemeData(
fontFamily: GoogleFonts.poppins().fontFamily,
brightness: Brightness.light,
scaffoldBackgroundColor: const Color.fromARGB(255, 226, 226, 226),
appBarTheme: const AppBarTheme(
backgroundColor: Color.fromARGB(255, 226, 226, 226),
),
colorScheme: const ColorScheme(
background: Color.fromARGB(255, 226, 226, 226),
onBackground: Color.fromARGB(255, 217, 217, 217),
brightness: Brightness.light,
primary: Color.fromARGB(255, 0, 166, 133),
onPrimary: Color.fromARGB(255, 248, 248, 248),
secondary: Color.fromARGB(255, 100, 157, 0),
onSecondary: Colors.white,
error: Color.fromARGB(255, 255, 255, 255),
onError: Color.fromARGB(255, 255, 0, 0),
surface: Color.fromARGB(255, 222, 222, 222),
onSurface: Color.fromARGB(255, 0, 0, 0),
onSurfaceVariant: Color.fromARGB(255, 215, 215, 215),
surfaceVariant: Color.fromARGB(255, 241, 241, 241),
outline: Color.fromARGB(255, 122, 122, 122),
tertiary: Color.fromARGB(255, 214, 161, 0)
)
),
darkTheme: ThemeData(
fontFamily: GoogleFonts.tajawal().fontFamily,
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color.fromARGB(255, 18, 18, 18),
appBarTheme: const AppBarTheme(
backgroundColor: Color.fromARGB(255, 18, 18, 18),
),
colorScheme: const ColorScheme(
background: Color.fromARGB(255, 18, 18, 18),
onBackground: Color.fromARGB(255, 49, 49, 49),
brightness: Brightness.dark,
primary: Color.fromARGB(255, 1, 255, 204),
onPrimary: Colors.black,
secondary: Color.fromARGB(255, 178, 255, 44),
onSecondary: Colors.white,
error: Color.fromARGB(255, 255, 255, 255),
onError: Color.fromARGB(255, 255, 0, 0),
surface: Color.fromARGB(255, 37, 37, 37),
onSurface: Colors.white,
onSurfaceVariant: Color.fromARGB(255, 55, 55, 55),
surfaceVariant: Color.fromARGB(255, 34, 34, 34),
outline: Color.fromARGB(255, 158, 158, 158),
tertiary: Colors.amber
)
),
themeMode: ThemeMode.light,
Simply add this to your build function, or main function.
import 'package:flutter/services.dart';
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
...
}
you just have to add this if you want to use as default template in the MaterialApp Theme :
appBarTheme: AppBarTheme(brightness: Brightness.light)
Result will be like this :
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: AppBarTheme(brightness: Brightness.light), <========
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ...,
);
After trying many methods, I found below method here.
If you are using SafeArea use this method:
Scaffold(
body: Container(
color: Colors.white, // your desire status bar color
child: SafeArea(child: CHILD),
),
);
You can use SystemUiOverlayStyle
Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0.0,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.white,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.dark,
)),
body:.....
Here You can use flutter flutter_statusbar_manager 1.0.2 lib
Flutter Statusbar Manager, lets you control the status bar color, style (theme), visibility, and translucent properties across iOS and Android. With some added bonus for Android to control the Navigation Bar.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_statusbar_manager/flutter_statusbar_manager.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
MyApp();
factory MyApp.forDesignTime() {
// TODO: add arguments
return new MyApp();
}
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
double _statusBarHeight = 0.0;
bool _statusBarColorAnimated = false;
Color _statusBarColor = Colors.black;
double _statusBarOpacity = 1.0;
bool _statusBarHidden = false;
StatusBarAnimation _statusBarAnimation = StatusBarAnimation.NONE;
StatusBarStyle _statusBarStyle = StatusBarStyle.DEFAULT;
bool _statusBarTranslucent = false;
bool _loadingIndicator = false;
bool _fullscreenMode = false;
bool _navBarColorAnimated = false;
Color _navBarColor = Colors.black;
NavigationBarStyle _navBarStyle = NavigationBarStyle.DEFAULT;
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
double statusBarHeight;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
statusBarHeight = await FlutterStatusbarManager.getHeight;
} on PlatformException {
statusBarHeight = 0.0;
}
if (!mounted) return;
setState(() {
_statusBarHeight = statusBarHeight;
});
}
Widget renderTitle(String text) {
final textStyle = TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold);
return Text(text, style: textStyle);
}
void colorBarChanged(Color val) {
this.setState(() {
_statusBarColor = val;
});
updateStatusBar();
}
void updateStatusBar() {
FlutterStatusbarManager.setColor(
_statusBarColor.withOpacity(_statusBarOpacity),
animated: _statusBarColorAnimated);
}
void statusBarAnimationChanged(StatusBarAnimation val) {
this.setState(() {
_statusBarAnimation = val;
});
}
void statusBarStyleChanged(StatusBarStyle val) {
this.setState(() {
_statusBarStyle = val;
});
FlutterStatusbarManager.setStyle(val);
}
void colorNavBarChanged(Color val) {
this.setState(() {
_navBarColor = val;
});
updateNavBar();
}
void updateNavBar() {
FlutterStatusbarManager.setNavigationBarColor(_navBarColor,
animated: _navBarColorAnimated);
}
void navigationBarStyleChanged(NavigationBarStyle val) {
this.setState(() {
_navBarStyle = val;
});
FlutterStatusbarManager.setNavigationBarStyle(val);
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Statusbar Manager example'),
),
body: new Container(
child: new Scrollbar(
child: new ListView(
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
children: <Widget>[
renderTitle("Status Bar Height: $_statusBarHeight"),
Divider(height: 25.0),
renderTitle("Status Bar Color:"),
SwitchListTile(
value: _statusBarColorAnimated,
title: new Text("Animated:"),
onChanged: (bool value) {
this.setState(() {
_statusBarColorAnimated = value;
});
},
),
Text("Color:"),
RadioListTile(
value: Colors.black,
title: Text("Black"),
onChanged: colorBarChanged,
dense: true,
groupValue: _statusBarColor),
RadioListTile(
value: Colors.orange,
title: Text("Orange"),
onChanged: colorBarChanged,
dense: true,
groupValue: _statusBarColor),
RadioListTile(
value: Colors.greenAccent,
title: Text("Green"),
onChanged: colorBarChanged,
dense: true,
groupValue: _statusBarColor),
RadioListTile(
value: Colors.white30,
title: Text("White"),
onChanged: colorBarChanged,
dense: true,
groupValue: _statusBarColor),
Text("Opacity:"),
Slider(
value: _statusBarOpacity,
min: 0.0,
max: 1.0,
onChanged: (double val) {
this.setState(() {
_statusBarOpacity = val;
});
updateStatusBar();
},
),
Divider(height: 25.0),
renderTitle("Status Bar Hidden:"),
SwitchListTile(
title: new Text("Hidden:"),
value: _statusBarHidden,
onChanged: (bool val) {
this.setState(() {
_statusBarHidden = val;
});
FlutterStatusbarManager.setHidden(_statusBarHidden,
animation: _statusBarAnimation);
},
),
Text("Animation:"),
RadioListTile(
value: StatusBarAnimation.NONE,
title: Text("NONE"),
onChanged: statusBarAnimationChanged,
dense: true,
groupValue: _statusBarAnimation),
RadioListTile(
value: StatusBarAnimation.FADE,
title: Text("FADE"),
onChanged: statusBarAnimationChanged,
dense: true,
groupValue: _statusBarAnimation),
RadioListTile(
value: StatusBarAnimation.SLIDE,
title: Text("SLIDE"),
onChanged: statusBarAnimationChanged,
dense: true,
groupValue: _statusBarAnimation),
Divider(height: 25.0),
renderTitle("Status Bar Style:"),
RadioListTile(
value: StatusBarStyle.DEFAULT,
title: Text("DEFAULT"),
onChanged: statusBarStyleChanged,
dense: true,
groupValue: _statusBarStyle),
RadioListTile(
value: StatusBarStyle.LIGHT_CONTENT,
title: Text("LIGHT_CONTENT"),
onChanged: statusBarStyleChanged,
dense: true,
groupValue: _statusBarStyle),
RadioListTile(
value: StatusBarStyle.DARK_CONTENT,
title: Text("DARK_CONTENT"),
onChanged: statusBarStyleChanged,
dense: true,
groupValue: _statusBarStyle),
Divider(height: 25.0),
renderTitle("Status Bar Translucent:"),
SwitchListTile(
title: new Text("Translucent:"),
value: _statusBarTranslucent,
onChanged: (bool val) {
this.setState(() {
_statusBarTranslucent = val;
});
FlutterStatusbarManager
.setTranslucent(_statusBarTranslucent);
},
),
Divider(height: 25.0),
renderTitle("Status Bar Activity Indicator:"),
SwitchListTile(
title: new Text("Indicator:"),
value: _loadingIndicator,
onChanged: (bool val) {
this.setState(() {
_loadingIndicator = val;
});
FlutterStatusbarManager
.setNetworkActivityIndicatorVisible(_loadingIndicator);
},
),
Divider(height: 25.0),
renderTitle("Navigation Bar Color:"),
SwitchListTile(
value: _navBarColorAnimated,
title: new Text("Animated:"),
onChanged: (bool value) {
this.setState(() {
_navBarColorAnimated = value;
});
},
),
Text("Color:"),
RadioListTile(
value: Colors.black,
title: Text("Black"),
onChanged: colorNavBarChanged,
dense: true,
groupValue: _navBarColor),
RadioListTile(
value: Colors.orange,
title: Text("Orange"),
onChanged: colorNavBarChanged,
dense: true,
groupValue: _navBarColor),
RadioListTile(
value: Colors.greenAccent,
title: Text("Green"),
onChanged: colorNavBarChanged,
dense: true,
groupValue: _navBarColor),
RadioListTile(
value: Colors.white12,
title: Text("white"),
onChanged: colorNavBarChanged,
dense: true,
groupValue: _navBarColor),
Divider(height: 25.0),
renderTitle("Navigation Bar Style:"),
RadioListTile(
value: NavigationBarStyle.DEFAULT,
title: Text("DEFAULT"),
onChanged: navigationBarStyleChanged,
dense: true,
groupValue: _navBarStyle),
RadioListTile(
value: NavigationBarStyle.LIGHT,
title: Text("LIGHT"),
onChanged: navigationBarStyleChanged,
dense: true,
groupValue: _navBarStyle),
RadioListTile(
value: NavigationBarStyle.DARK,
title: Text("DARK"),
onChanged: navigationBarStyleChanged,
dense: true,
groupValue: _navBarStyle),
Divider(height: 25.0),
renderTitle("Fullscreen mode:"),
SwitchListTile(
title: new Text("Fullscreen:"),
value: _fullscreenMode,
onChanged: (bool val) {
this.setState(() {
_fullscreenMode = val;
});
FlutterStatusbarManager.setFullscreen(_fullscreenMode);
},
),
],
),
),
),
),
);
}
}

Resources