How to make Flutter Web App to hide Address Bar and Tab Bar while scrolling down like HTML web pages do? - flutter-web

Is it possible to make Flutter Web App to behave more like web page in respect of hiding browser assets during scrolling?
Mobile browsers hide address bar and tab bar while scrolling down web pages - this makes more room for content while these browser elements are likely not needed.
For Flutter Web App it looks like that:
White for standard HTML page:
One more thing:
To connect with development web server from mobile device over the local network:
flutter run -d web-server --web-hostname 0.0.0.0 --web-port 5000
(for me it works only with actual local IP instead of zeros)
Screens above were made with some dummy code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Blog Posts',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const BlogPostList(),
);
}
}
class BlogPostList extends StatelessWidget {
const BlogPostList({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Travel Blog'),
),
body: ListView(
children: const [
BlogPostListItem(
title: '5 Must-Visit Destinations in Europe',
date: 'Date',
description:
'Are you planning a trip to Europe? Check out our top picks for the most breathtaking and unforgettable destinations on the continent.',
),
BlogPostListItem(
title: 'The Best Time to Visit Bali: A Season-by-Season Guide',
date: 'Date',
description:
'Bali is a popular travel destination, but when is the best time to go? We break down the pros and cons of each season to help you decide when to book your trip.',
),
BlogPostListItem(
title: 'A Guide to Sustainable Tourism in Costa Rica',
date: 'Date',
description:
'Costa Rica is known for its beautiful natural landscapes and eco-friendly tourism practices. In this blog post, we explore the country\'s efforts to preserve its environment and provide tips for travelers who want to make a positive impact.',
),
BlogPostListItem(
title: 'Exploring the Food Scene in Tokyo: A Local\'s Guide',
date: 'Date',
description:
'Tokyo is a culinary paradise, with endless options for every type of foodie. In this post, we share insider tips and recommendations from a local on where to find the best sushi, ramen, and more.',
),
BlogPostListItem(
title:
'The Ultimate Road Trip Itinerary: California\'s Pacific Coast Highway',
date: 'Date',
description:
'The Pacific Coast Highway is one of the most iconic road trips in the world. In this blog post, we share a detailed itinerary for a week-long journey along this scenic route.',
),
BlogPostListItem(
title: 'The Best Beaches in the Caribbean: Our Top Picks',
date: 'Date',
description:
'With crystal clear waters and white sandy beaches, the Caribbean is a paradise for beach lovers. In this post, we share our top picks for the best beaches in the region.',
),
BlogPostListItem(
title: 'Discovering the History and Culture of Marrakech',
date: 'Date',
description:
'Marrakech is a city full of history and culture, with a vibrant medina, beautiful gardens, and iconic landmarks. In this blog post, we delve into the city\'s rich past and share tips for exploring its many attractions.',
),
BlogPostListItem(
title: 'A Trekker\'s Guide to Nepal: Planning Your Adventure',
date: 'Date',
description:
'Nepal is a paradise for trekkers, with breathtaking mountain landscapes and a rich culture. In this post, we provide tips for planning your trek, including what to pack, where to go, and how to prepare for the high altitude.',
),
BlogPostListItem(
title: 'The Best Places to See Wildlife in Africa',
date: 'Date',
description:
'Africa is home to an incredible variety of wildlife, from majestic elephants and giraffes to rare species like gorillas and chimpanzees. In this post, we share our top picks for the best places to see wildlife on the continent.',
),
BlogPostListItem(
title: 'The Most Beautiful National Parks in the United States',
date: 'Date',
description:
'From Yellowstone to Yosemite, the United States is home to some of the most breathtaking national parks in the world. In this blog post, we share our top picks for the most beautiful parks in the country.',
),
],
),
);
}
}
class BlogPostListItem extends StatelessWidget {
final String title;
final String date;
final String description;
const BlogPostListItem({
super.key,
required this.title,
required this.date,
required this.description,
});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.headline6,
),
const SizedBox(height: 8),
Text(
date,
style: Theme.of(context).textTheme.subtitle2,
),
const SizedBox(height: 8),
Text(
description,
style: Theme.of(context).textTheme.bodyText2,
),
],
),
),
),
);
}
}
(I'm not including HTML -any web page behaves the desired way :-)
And just for the case: I'm aware that problem does not persist if user decides to "install" PWA generated by Flutter - I need solution for ad-hoc use case (i.e. mobile landing page).
I have tried:
import 'dart:html' as html;
void main() {
runApp(MyApp());
html.window.scrollTo(0, 0);
html, body {
margin: 0;
padding: 0;
scroll-behavior: smooth;
}
#app {
height: 100vh;
overflow-y: scroll;
}
Not much of Web Dev to try to inject some JS on top of Flutter :-)

Related

Unnecessary Stateless Widget Rebuilds (Flutter/Dart) - Possibly StreamBuilder Related?

I am having an issue with very high levels of widget rebuild. In particular when any navigation between pages happens.
App Background:
I am essentially building an app to create and display collections (in this case house plants). Authentication, database, and image storage are all via Firebase. This data is then delivered to widgets via streams. All my custom widgets are currently stateless.
Summary of Main Page Structure:
I have a main "library" page that displays this information. This page has a column with a list of "Group" widgets. The major widget structure for the column looks like this:
Group
Collection
PlantTile
PlantTile
Collection
PlantTile
PlantTile
PlantTile
Group
Collection
PlantTile
Collection
PlantTile
PlantTile
etc...
Obviously the number of groups, collections, and plantTiles will vary depending on what the user has submitted to the database.
Problem:
Every time I navigate between pages in the app, each plant Tile rebuilds not once, but 5 times. In the example image there are 49 of these tiles, resulting in 245 rebuilds. The plantTiles are stateless but wrapped in a StreamBuilder to display data. These widgets contain images and box shadows which seem to be very taxing on the system to rebuild.
Widget Rebuild Stats
GridView.builder wraps each plantTile with a StreamBuilder
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:plant_collector/formats/constants.dart';
import 'package:plant_collector/screens/plant.dart';
import 'package:plant_collector/widgets/dialogs/select/dialog_select.dart';
import 'package:provider/provider.dart';
import 'package:plant_collector/models/ui_builders/builders_general.dart';
import 'package:plant_collector/formats/colors.dart';
class TilePlant extends StatelessWidget {
final Map plantMap;
final String collectionID;
final List<dynamic> possibleParents;
TilePlant({
#required this.plantMap,
#required this.collectionID,
#required this.possibleParents,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
showDialog(
context: context,
builder: (BuildContext context) {
return DialogSelect(
title: 'Move to Another Collection',
text:
'Please select the collection where you would like to move this plant.',
plantID: plantMap[kPlantID],
listBuildColumn: Provider.of<UIBuilders>(context)
.createDialogCollectionButtons(
selectedItemID: plantMap[kPlantID],
currentParentID: collectionID,
possibleParents: possibleParents,
),
);
},
);
},
child: Container(
decoration: BoxDecoration(
image: plantMap[kPlantThumbnail] != null
? DecorationImage(
image: CachedNetworkImageProvider(plantMap[kPlantThumbnail]))
: DecorationImage(
image: AssetImage(
'assets/images/default.png',
),
),
boxShadow: kShadowBox,
shape: BoxShape.rectangle,
),
child: FlatButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PlantScreen(
plantID: plantMap[kPlantID],
forwardingCollectionID: collectionID,
),
),
);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
plantMap[kPlantName] != null
? Padding(
padding: EdgeInsets.all(1.0),
child: Container(
color: Color(0x44000000),
margin: EdgeInsets.all(2.0),
padding: EdgeInsets.all(3.0),
constraints: BoxConstraints(
maxHeight: 50.0,
),
child: Text(
plantMap[kPlantName],
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: TextStyle(
fontSize: 10.0,
color: Colors.white,
),
),
),
)
: Container(),
],
),
),
),
);
}
}
What I Would Like:
These widgets have no reason to update. The point of wrapping in the stream was that after the initial creation, a plantTile should only be updated if a stream event is delivered (the user modifies plant data). So ideally the widget rebuild of plantTile on navigation would be zero (not 5 x 49 = 245), and if the data of a plant is changed, 1 rebuild.
I have read that there may be an issue with the StreamBuilders firing at every screen build. I tried to follow other guides to solve this but I must be missing something because I can't seem to stop it.
Any help would be much appreciated! I am new to this, so hopefully I'm not doing something fundamentally wrong.

Flutter-How to change the Statusbar text color in Dark Mode?

I hope to control the statusbar text color in iOS 13 Dark Mode. I could change the statusbar color by setting the Scaffold's AppBar property "brightness" When not open the Dark Mode.
Codes just like below:
return Scaffold(
appBar: AppBar(
brightness: Brightness.light, //<--Here!!!
title: Text(widget.title),
),
...
The effort just like this:
light brightness:
dark brightness:
But when I enable the simulator's Dark Mode, the method is not working.
Open the simulator's "Dark Appearance":
After opening the "Dark Appearance", the statusbar text color couldn't change any more by the method, it's just in white color(lightness mode).
I have tried those method to change statusbar text color:
Method 1:
void main() {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown
]);
runApp(MyApp());
}
Method 2:
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: Material(
child: Scaffold(
appBar: AppBar(
brightness: Brightness.light,
title: Text(widget.title),
),
body: Center(
But neither of them could work.
Hope your help! Thank you.
At first go to ios/Runner folder. Next open info.plist and add the following lines into the Dict section.
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
Next. Make sure you have these lines in Theme settings of your MaterialApp:
...
MaterialApp(
themeMode: ThemeMode.light, // Change it as you want
theme: ThemeData(
primaryColor: Colors.white,
primaryColorBrightness: Brightness.light,
brightness: Brightness.light,
primaryColorDark: Colors.black,
canvasColor: Colors.white,
// next line is important!
appBarTheme: AppBarTheme(brightness: Brightness.light)),
darkTheme: ThemeData(
primaryColor: Colors.black,
primaryColorBrightness: Brightness.dark,
primaryColorLight: Colors.black,
brightness: Brightness.dark,
primaryColorDark: Colors.black,
indicatorColor: Colors.white,
canvasColor: Colors.black,
// next line is important!
appBarTheme: AppBarTheme(brightness: Brightness.dark)),
...
Good luck!
P.S.
You don't have to set brightness Here!! anymore :)
Scaffold(
appBar: AppBar(
brightness: Brightness.light, //<--Here!!!
title: Text(widget.title),
),
I found my question just the same as the flutter Issue#41067
---"Flutter not automatically changing the status bar icons to black on devices running iOS 13.0 in Dark Mode it only does so when Dark Mode on iOS 13 is turned off #41067"
And the Issue state is Opening, so just hope it will be resolved as soon as possible.
The issue link just below:
flutter issue#41067
Under ios/Runner in Info.plist add
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
it worked for me.
If your app is a materialApp you can set the darkTheme property of your MaterialApp:
return new MaterialApp(
darkTheme: ThemeData.dark(),
);
Here is the link to the doc: https://api.flutter.dev/flutter/material/MaterialApp-class.html
If it don't solve your problem you can create your own Dark theme using:
return new MaterialApp(
darkTheme: ThemeData(
// Your theme config
),
);
Link to ThemeData doc: https://api.flutter.dev/flutter/material/ThemeData-class.html
import 'package:flutter_statusbarcolor/flutter_statusbarcolor.dart';
This worked for me. If you want black text use:
#override
Widget build(BuildContext context) {
FlutterStatusbarcolor.setStatusBarWhiteForeground(false);
return MaterialApp();
}
For white text use:
#override
Widget build(BuildContext context) {
FlutterStatusbarcolor.setStatusBarWhiteForeground(true);
return MaterialApp();
}
change status bar text color/icon brightness.flutter 2.2.3
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
appBarTheme: Theme.of(context).appBarTheme.copyWith(
brightness: Brightness.dark,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarIconBrightness: Brightness.light,
),
),
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
In my case I wanted a dark font in my status bar on my light themed app. The problem is that my app bar's background color is transparent so the status bar default font color is white (I tried setting the app bars color to white and voilĂ  - the status bar text font was black).
Since I wanted to keep my transparent status bar, my solution was to add systemOverlayStyle to my theme's appBarTheme like this.:
AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarBrightness: Brightness.light,
),
The app_bar_theme.dart file told me this solution in the brightness' deprecation notes:
#Deprecated(
'This property is no longer used, please use systemOverlayStyle instead. '
'This feature was deprecated after v2.4.0-0.0.pre.',
)
this.brightness,

How to make a custom table widget in flutter?

I'm making a custom widget for showing a weekly schedule. I want it to look somewhat like a normal table, with the days in the header and the time of day on the vertical axis. Then I want to add events in the calendar, but as an event can span several cells, I can not use the built in table (it has no col/row span).
So I started to make my own table using CustomPaint. It went OK until I came to the header part, which I am stuck on the layout with my idea. In the code I supplied you can see that the header will have the same width as the side bar (with time) and the WeekView (the CustomPaint table). This means I have no idea how to align the header (Monday, Tuesday etc..) with the grid in the WeekView as the space the side bar takes up is not known. And as I want the side bar to scroll with the table, it has to be inside the scrollview. Can I solve this any other way? Maybe with slivers (that I know very little of yet)?
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
WeekSelector(
week: controller.week,
onPrevPressed: onPrevPressed,
onNextPressed: onNextPressed,
),
WeekHeader(),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
child: Row(
children: <Widget>[
Text("09:00"), // here will be a bar for the time
Expanded(
child: WeekView(
startHour: 9,
endHour: 18,
),
),
],
),
),
),
),
],
);
}

How do I make the Navigation Drawer of my Flutter app transparent?

I have built a native Android App which has a transparent navigation drawer. I have been asked to build the same app using Flutter and I have gotten to the point where I would like to implement a transparent navigation drawer. How do I make the Navigation Drawer of my Flutter app transparent because I have been struggling on that end ? I have already tried
drawer: Drawer(
child: Container(
color: Colors.transparent,)),
The navigation drawer just remains white. I have been searching for a solution to this and cant find one. Any help would be appreciated.
I have attached images of the Native App with a transparent drawer and the Flutter version with a white Navigation drawer
I think there's a better way of doing this without messing up the entire canvases on the app. Since you want it specifically for the drawer, try this approach.
Scaffold(
drawer: Theme(
data: Theme.of(context).copyWith(
// Set the transparency here
canvasColor: Colors.transparent, //or any other color you want. e.g Colors.blue.withOpacity(0.5)
),
child: Drawer(
// All other codes goes here.
)
)
);
use the transparent color like you are currently doing but also in the drawer widget use a stack and inside it make the first widget a backdropfilter, you will need to import dart:ui. here is an example
//import this library to use the gaussian blur background
import 'dart:ui';
Scaffold(
appBar: AppBar(
title: Text('Title'),
),
drawer: Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
child: sideNav()),
body: Text('Hello Body'),
),
Drawer sideNav(){
return Drawer(
child: Stack(
children: <Widget> [
//first child be the blur background
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), //this is dependent on the import statment above
child: Container(
decoration: BoxDecoration(color: Color.grey.withOpacity(0.5))
)
),
ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Hello Drawer Title')
),
ListTitle(
leading: Icon(Icons.dashboard, color: Colors.white)
title: "Dashboard"
onTap: (){
}
)
]
)
]
)
);
}
After much tinkering around I managed to find a solution.
I edited the ThemeData and added a canvas color as described below
theme: new ThemeData(
canvasColor: Colors.transparent
),
This isn't the best way to do this, it is more of a workaround than anything.
Visual Representation
Screenshot Of drawer top whitespace
If you came here and finding the solution about how to remove the white space above the drawer and status bar then just simply use SingleChildScrollView ---> Column().
Because if you add something like ListView() then the white Space will take place above your drawer which is so irritating to see.
I know this is not the actual solution of this problem but it will help someone who needs it.
Just wrap the drawer with opacity and give the opacity a value (between 0 and 1)
Opacity(
opacity: 0.7,
child: Drawer(//your drawer here),
),

Flutter: How do you make a card clickable?

I just have a simple Card like new Card(child: new Text('My cool card')) and I want to be able to click anywhere on it to run some function, except there's no onPressed method for a Card. I could add a button to the bottom, but that's not ideal for this situation.
Anyone know how to make the whole card clickable?
Flutter use composition over properties.
Wrap the desired widget into a clickable one to achieve what you need.
Some clickable widgets : GestureDetector, InkWell, InkResponse.
GestureDetector(
onTap: () => ......,
child: Card(...),
);
Flutter provides the InkWell Widget. by registering a callback you can decide what happens when user clicks on the card (called tap in flutter). InkWell also implements Material Design ripple effect
Card(
child: new InkWell(
onTap: () {
print("tapped");
},
child: Container(
width: 100.0,
height: 100.0,
),
),
),
I think you can also use InkWell apart from GestureDetector just wrap the card inside InkWell() Widget
InkWell(
onTap: (){ print("Card Clicked"); }
child: new Card(),
);
You can use Inkwell and insert splashColor which, at the click of the user, creates the rebound effect with the chosen color, on the card ..
This is mainly used in material design.
return Card(
color: item.completed ? Colors.white70 : Colors.white,
elevation: 8,
child: InkWell(
splashColor: "Insert color when user tap on card",
onTap: () async {
},
),
);
Wrap a card in GestureDetector Widget like a below:
GestureDetector(
onTap: () {
// To do
},
child: Card(
),
),
Another way is as follows:
InkWell(
onTap: () {
// To do
},
child: Card(),
),
In Flutter, InkWell is a material widget that responds to touch action.
InkWell(
child: Card(......),
onTap: () {
print("Click event on Container");
},
);
GestureDetector is a widget that detects the gestures.
GestureDetector(
onTap: () {
print("Click event on Container");
},
child: Card(.......),
)
Difference
InkWell is a material widget and it can show you a Ripple Effect whenever a touch was received.
GestureDetector is more general-purpose, not only for touch but also for other gestures.
The most preferred way is to add ListTile as Card child. Not only does ListTile contain the method onTap it also helps you in making Card interesting.
Card(
child: ListTile(
title: Text('Title')
leading: CircleAvatar(
backgroundImage: AssetImage('assets/images/test.jpg'),
),
onTap: () {
print('Card Clicked');
},
),
),
You also can insert a card into a TextButton:
TextButton clickableCard = TextButton(child: card, onPressed: onCardClick, style: [...]);
This brings the advantage, that you get some features for free. For example in Flutter Web, you get a mousover effect and the cursor changes to the hand so that ths user knows, he can click there. Other additional features can be customised using the style.
Do something on tap/click of 'child' in Flutter:-
Code:-your code looks like:
child: Card(------
------------
--------),
Step1:- Put your mouse cursor on Card then, press- Alt+Enter(in windows) select wrap with widget.
Step2:- Change your default widget into GestureDetector.
final code:-
child: GestureDetector(
onTap: YourOnClickCode(),
child: Card(------
------------
--------),
),
Most of the answers are brilliant but I just want to share mine for the one who wants to make/show a ripple effect on Tap of card or list tile.
Card(
child: TextButton(
onPressed: ()=> ...,
child: ListTile(
title: Text('title'),
),
),
);

Resources