I'm trying to find a way to place an AdMob ad at the top of my widget tree. The best way I thought of doing this was to try to place it in the flexible space inside the appbar. The spacing isn't quite how I want it as the ad is appearing behind the title, leading, and trailing widgets on the appbar. Is there any way to move the ad up so it's not placed behind the content of the appbar? If not, is there a way I can place the ad outside the appbar and have it appear above a scaffold (tried wrapping the scaffold in the column to achieve this and didn't work)?
Image of appbar:
AppBar returnAppBarForHomePagesTest(
BuildContext context,
String page_header,
localUser local_user,
Function updateUserState,
bool using_default_image,
String profile_pic,
GlobalKey<ScaffoldState> scaffold_key,
BannerAd ad,
bool ad_loaded) {
return AppBar(
toolbarHeight: 100,
flexibleSpace: ad_loaded /// TRYING TO PLACE AD HERE
? Container(
child: AdWidget(
ad: ad,
),
width: ad.size.width.toDouble(),
alignment: Alignment.center,
)
: const CircularProgressIndicator(),
elevation: 0,
backgroundColor: HEADER_FOOTER_BACKGROUND_COLOR,
centerTitle: true,
title: Text(page_header,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: PRIMARY_PAGE_HEADER_SIZE,
color: Colors.white,
)),
leadingWidth: 35,
automaticallyImplyLeading: false,
leading: Transform.translate(
offset: const Offset(8, 0),
child: GestureDetector(
onTap: () {
globals.GlobalVars()
.navigatorKey
.currentState
?.pushNamed(EditProfile.route_name, arguments: local_user)
.then((value) {
if (value != null) {
updateUserState(value);
}
});
},
child: CircleAvatar(
backgroundImage: using_default_image
? AssetImage(profile_pic) as ImageProvider
: NetworkImage(profile_pic),
radius: 5,
),
),
),
actions: [
IconButton(
icon: const Icon(
Icons.settings_outlined,
color: Colors.white,
size: 35,
),
onPressed: () {
scaffold_key.currentState?.openDrawer(); // do something
},
)
],
);
}
The easiest way would be to wrap your Scaffold in a Stack
return Stack(
children: [
Scaffold(
appBar: AppBar(
...
),
body: ...
),
if (showAd)
//Your ad here
Container(
height: kToolbarHeight,
width: MediaQuery.of(context).size.width,
child : ...
),
],
);
Here is a working gist you can try
Related
I want to make the bottom portion (that includes an ad) of the WebView clickable without a touch/tap. My WebView contains a floating ad in its bottom portion. I want to make it auto clickable without tapping it again and again. I have used IgnorePointer class. Some portion of my code is commented, you can check it also.
(Note: I'm doing this just for learning)
here is my code:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeScreen extends StatelessWidget{
#override
Widget build(BuildContext context) {
final _key = UniqueKey();
final height=MediaQuery.of(context).size.height;
// return CustomScrollView(
//
// slivers: <Widget>[
// SliverAppBar(
// title: const Text("Software Testing Help"),
// floating: true,
// ),
// SliverFillRemaining(
// child: WebView(initialUrl: "https://www.softwaretestinghelp.com/digital-marketing-software/"),
// )
// ],
// );
return Scaffold(
appBar: AppBar(
toolbarHeight: height*0.08,
backgroundColor: Colors.black,
title: Center(child: Text('Software Testing Help', style: TextStyle(
color: Colors.white
),)),
),
body:
Stack(
children: [
Container(
height: height*0.90,
child: Expanded(
child: WebView(
initialUrl: 'https://www.softwaretestinghelp.com/digital-marketing-software/',
key: _key,
javascriptMode: JavascriptMode.unrestricted,
),
)
),
Positioned(
bottom:0,
left: 10,
child: GestureDetector(
onTap: (){
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Ad is clicked'),
));
},
child: IgnorePointer(
ignoring: true,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue
)
),
height: 100,
width: 100,
),
),
),
)
]
)
);
}
}
you can do it automatically by a timer, after a period of time this timer will call your function.
something like this:
bool? isLoading;
adClickedRunner() {
Timer(
Duration(seconds: 5), //example: after 5sec will call your task
() async {
isLoading = true; //to show circularProgressIndicatior for your button
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Ad is clicked'),));
isLoading = false;
},
);
}
I am using Cards in Flutter and want Progress Indicator at the left bottom position for 2 seconds while Tap on the card so that another page load successfully.
Does anyone know how to add?
Container(
height: 130,
child: Card(
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.setting),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () async {
// I try this one but not working
// Flushbar(
//
// showProgressIndicator: true,
// duration: Duration(seconds: 2),
// );
getDetails().then((myCardlocations) {
Navigator
.of(context)
.pushNamed('/myCardlocations',
arguments: ObjectLocations(locations, 'myCardlocations'));
}
);
}
),
),
],
),
),
),
You can do something like this using Stack and CircularProgressIndicator..
class _MyWidgetState extends State<MyWidget> {
bool isLoading = false;
#override
Widget build(BuildContext context) {
return Container(
height: 130,
child: Stack(
children: [
Container(
height: 130,
child: Card(
child: Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () async {
setState(() {
isLoading = true;
});
getDetails().then((myCardLocations) {
setState(() {
isLoading = false;
});
// navigation code here
});
},
),
),
],
),
),
),
Align(
alignment: Alignment.bottomLeft,
child: isLoading
? Padding(
padding: EdgeInsets.fromLTRB(15,0,0,15),
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(),
),
)
: SizedBox(),
),
],
),
);
}
}
Edit:
Looks like I misunderstood the question a bit. Specifically, the place where to show the progress indicator. Anyways, if you get the idea, you can put the indicator at a different place as per your requirement.
There are certain things, which I would like to mention before I give the actual answer.
Read about Flutter.delayed constructor, very useful thing to make some thing wait for a while and do the operation by providing Duration. Whatever you want to do after that duration, it will implement in the callback function
Future.delayed(Duration(seconds: your_time, (){
//it will perform this operation after that much of seconds
}));
You can always show/hide a Widget using bool value, and make changes accordingly
Use a column and Add the LinearProgressIndicator at the end of the Widget. Show/hide it based up on the data
Also, use MediaQuery to give out the height. It is more efficient way of giving the dimensions according to all phone size. Like match-parent in Android Studio. Do the math accordingly, I have shown in the code also
Column(
children: [
Row(),
bool val ? LinearProgressIndicator() : Container() // Container() is nothing but an empty widget which shows nothing
]
)
Some heads up: I have not used getData, since it is not defined properly but you can call it the in function which I will show you in the code, that is pageTransit(). Follow the comments and you are good to go
class _MyHomePageState extends State<MyHomePage> {
// this takes care of the show/hide of your progress indicator
bool _showProgress = false;
// this takes care of the operation
void pageTransit(){
// first show when the ListTile is clicked
setState(() => _showProgress = true);
Future.delayed(Duration(seconds: 2), (){
// hide it after 2 seconds
setState(() => _showProgress = false);
// do the page trnasition here
//getDetails().then((myCardlocations) {
//Navigator.of(context).pushNamed('/myCardlocations',
//arguments: ObjectLocations(locations, 'myCardlocations'));
//}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height * 0.1,
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// use your items here, based upon the bool value show hide your
// progress indicator
Row(
children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
onTap: () => pageTransit()
)
)
]
),
// show/hide in the card
_showProgress ? LinearProgressIndicator() : Container()
]
)
)
)
);
}
}
Result
Look at the ProgressIndicator, it remains there for 2 seconds, and then goes away
1. You need to define a GlobalKey for the Scaffold so that you can use a SnackBar (you can define the GloablKey in your page's State).
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
2. You need to set the key for the Scaffold.
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
...
3. You need to wrap the Card with a GestureDetector and set the onTap function to call showLoading which shows a SnackBar on the bottom of the screen. Call your getDetails function in the showLoading. Full code (except the define key step):
void _showLoading() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
duration: new Duration(seconds: 2),
content: new Row(
children: <Widget>[
new CircularProgressIndicator(),
new Text("Loading...")
],
),
));
// call to your getDetails and its steps should be here
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("My app"),
),
body: Center(
child: GestureDetector(
child: Card(
child: Row(children: <Widget>[
Expanded(
child: ListTile(
title: Text(
'My card Location',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w700),
),
leading: Icon(Icons.settings),
// color: Colors.blueAccent, size: mediumIconSize),
trailing: Icon(Icons.keyboard_arrow_right),
selected: true,
)),
])),
onTap: () => _showLoading(),
)),
);
}
}
Note: you can also style the SnackBar.
Result:
I was trying to list dropdown like widget, but luckily found the expansion panel list widget to get my desired UX to feel.
So, I am using ExpansionPanelList in my flutter app, but don't require the default elevation/border-shadow it comes with.
I have no idea how to remove it, so as to make it look part of the body rather than an elevated container.
Currently looking like this:
Following is my code:
class _PracticetestComp extends State<Practicetest> {
var listofpracticetest;
List<Item> _data = [
Item(
headerValue: 'Previous Question Papers',
expandedValue: '',
)
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xffF8FDF7),
appBar: AppBar(
backgroundColor: Color(0xffF8FDF7), // status bar color
brightness: Brightness.light,
elevation: 0.0,
leading: Container(
margin: EdgeInsets.only(left: 17),
child: RawMaterialButton(
onPressed: () {
Navigator.pushNamed(context, '/');
},
child: new Icon(
Icons.keyboard_backspace,
color: Colors.red[900],
size: 25.0,
),
shape: new CircleBorder(),
elevation: 4.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(5.0),
),
),
),
body: Container(
// height: 200,
margin: EdgeInsets.only(top: 40),
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 30),
child: Theme(
data: Theme.of(context)
.copyWith(cardColor: Color(0xffF8FDF7)),
child: _buildPanelPreviousPapers()))
],
)
],
),
));
}
Widget _buildPanelPreviousPapers() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_data[index].isExpanded = !isExpanded;
});
},
children: _data.map<ExpansionPanel>((Item item) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: Container(
child: ListTile(
leading: Text(
'Alegbra',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
),
),
),
),
isExpanded: item.isExpanded,
);
}).toList(),
);
}
}
// stores ExpansionPanel state information
class Item {
Item({
this.expandedValue,
this.headerValue,
this.isExpanded = false,
});
String expandedValue;
String headerValue;
bool isExpanded;
}
wrap your entire expansion widget child inside Material widget and change the elevation based on expansion child is expanded or not with method
Material(
elevation: isSelected ? 4 : 0,
child: ExpansionTile(
onExpansionChanged:(value){
isSelected=value;
setState(){};
},
title: getExpantionTitle(context),
children: getChildrentList(),
),
),
),
in case if you don't like divider in ExpansionTile tile do some thing like this
final theme = Theme.of(context).copyWith(dividerColor:
Colors.transparent);
//use as a child
child:Theme(data: theme, child: ExpansionTile(...));
Just add this line:
ExpansionPanelList(
elevation: 0, // this line
expansionCallback: ...
First, it isn't recommended to not use elevation for ExpansionPanelList according to Material design spec.
However, if you really want to do that, there are 2 solutions for you, either you create your own custom ExpansionPanelList, or get ready to add couple of lines to the source file. I'm providing you the latter solution.
Open expansion_panel.dart file, go to the build() method of _ExpansionPanelListState and make following changes
return MergeableMaterial(
hasDividers: true,
children: items,
elevation: 0, // 1st add this line
);
Now open mergeable_material.dart file, navigate to _paintShadows method of _RenderMergeableMaterialListBody class and make following changes:
void _paintShadows(Canvas canvas, Rect rect) {
// 2nd add this line
if (boxShadows == null) return;
for (final BoxShadow boxShadow in boxShadows) {
final Paint paint = boxShadow.toPaint();
canvas.drawRRect(kMaterialEdges[MaterialType.card].toRRect(rect), paint);
}
}
Screenshot:
Unfortunately ExpansionPanelList elevation hardcoded, but you can make same widget with ExpansionTile, check this dartpad example.
https://dartpad.dev/0412a5ed17e28af4a46f053ef0f7a5c2
I would wrap it in a ClipRect.
Widget _buildPanelPreviousPapers() {
final panel = ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_data[index].isExpanded = !isExpanded;
});
},
children: _data.map<ExpansionPanel>((Item item) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: Container(
child: ListTile(
leading: Text(
'Alegbra',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.w500),
),
),
),
),
isExpanded: item.isExpanded,
);
}).toList(),
);
return ClipRect(child: panel);
}
To alter the default card background color add a Theme override:
return ClipRect(
child: Theme(
data: Theme.of(context).copyWith(cardColor: Colors.pink),
child: child,
),
);
I was able to set elevation in the constructor (default value is 2), perhaps this is a recent API change:
https://api.flutter.dev/flutter/material/ExpansionPanelList/ExpansionPanelList.html
I have a BottomNavigationBar which I need to add only one centralized button inside, but I am getting this error:
'package:flutter/src/material/bottom_navigation_bar.dart': Failed assertion: line 191 pos 15: 'items.length >= 2': is not true.
which is logical as the flutter's source code has this condition:
//..
assert(items.length >= 2),
So, here's my code, is there a workaround for this using BottomNavigationBar to keep the code clean?
BottomNavigationBar(
items: <BottomNavigationBarItem>[
buildBottomNavigationBarItem(
iconData: Icons.close,
),
// AN ERROR AFTER COMMENTING THIS:
// buildBottomNavigationBarItem(
// iconData: Icons.open,
// ),
],
),
BottomNavigationBarItem buildBottomNavigationBarItem(
{IconData iconData, String title = ''}
) {
return BottomNavigationBarItem(
icon: Icon(
iconData,
color: Theme.of(context).accentColor,
size: 0.04 * _deviceHeight,
),
title: Text(
title,
),
);
}
thanks
You can't use BottomNavigationBar but instead of it you can create your own widget and pass it into the bottomNavigationBar parameter.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
body: SafeArea(
child: Text('Hi'),
),
bottomNavigationBar: Container(
height: 60,
color: Colors.black12,
child: InkWell(
onTap: () => print('tap on close'),
child: Padding(
padding: EdgeInsets.only(top: 8.0),
child: Column(
children: <Widget>[
Icon(
Icons.close,
color: Theme.of(context).accentColor,
),
Text('close'),
],
),
),
),
),
),
);
}
}
If the new, custom bottom navigation bar overlaps with the phone's OS GUI, you can wrap the InkWell with a SafeArea widget.
You can change the assert(items.length >= 2), to the length you want in the bottom navigation bar if you want to have three items turn the assert(items.length >= 2), to assert(items.length >= 3),
I'am trying to create a custom widget CarteSim and I'am trying to call it everytime I click on Floating Action Button , but unfortunately it doesn't show when I call it inside the button , but it shows when I call it in the Scaffold , please who has ever experience that
here s my code
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.pink,
title: Text(" INWI "),
),
body: SingleChildScrollView(
child: Container(
child: Center(
child: Column(
children: <Widget>[
DropdownButton<String>(
items: _type.map((String dropDownStringItem) {
return DropdownMenuItem<String>(
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}).toList(),
onChanged: (String newValueSelected) {
setState(() {
this._currentItemSelected = newValueSelected;
});
},
value: _currentItemSelected,
),
Divider(
color: Colors.black,
),
new Row(
children: [
new Text(" type client supporte * carte avec 1 cin"),
new Checkbox(value: _isChecked, onChanged: (bool value){ onChanged(value);}),
]
),
Divider(
color: Colors.black,
),
new Column(
children: <Widget>[
CarteCin(),
CarteSim(),
]
)
],
),
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: (){
Column(
children: <Widget>[
CarteSim(),
]
);
},
child: Icon(Icons.add),
backgroundColor: Colors.pink,
),
);
}
]
}
you are misunderstanding the process. The onPressed key actually used for call actions on press the floatingActionButton. You can write functions there, like you can update your state or print anything on your console or call an API to get data. But you can't display any widget from there.
Now what you're trying to do, that can be resolved like this. Whenever you press that button you should update your state, i.e bool show. Then modify your Column children based on that show state.
Here is an example how tu update the UI in flutter
class FrontPage extends StatefulWidget {
#override
_FrontPageState createState() => _FrontPageState();
}
class _FrontPageState extends State<FrontPage> {
bool currentStateShowsThis;
initState() {
currentStateShowsThis = false;
super.initState();
}
Widget build(BuildContext context) {
final color = currentStateShowsThis ? Colors.blue : Colors.red;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.pink,
title: Text(" INWI "),
),
body: SingleChildScrollView(
child: Container(
child: Center(
child: Column(
children: <Widget>[
Container(width: 100, height: 100, color: color,) // alternatively you can change the widgets being displayed
],
),
),
)
),
floatingActionButton: FloatingActionButton(
onPressed: (){
setState(() {
currentStateShowsThis = !currentStateShowsThis;
});
},
child: Icon(Icons.add),
backgroundColor: Colors.pink,
),
);
}
}
In your example "the button" will call back the
onPressed:
Which will only create the widgets in that method (Column ...) and not place them in a the layout.