I'm trying to implement the call button inside a card widget,
I want the whole card background to change color to blue (like selected) when I press the call button, and to be changed back to normal when I press any other card, like to make the call button switch for card selection,
tried to use the setState function but it didn't work since it changes color only when I'm tapping the whole card not a specific button in it.
How do I make the whole card selected when I press the call button and released when I press any other card (after I get back from the dialer application)
Here's my code:
_launchCaller() async {
const url = "tel:+972545522973";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Card(
margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
color: Colors.brown[30],
child: ListTile(
isThreeLine: true,
title: Row(
children: <Widget> [
Container(
child:Text(widget.helpRequest.category.description) ,
alignment: Alignment.topLeft,
),
Spacer(),
Container(
child:Text(formatter.format(now)),
alignment: Alignment.topRight,
),
]
)
,
subtitle: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
GetUserName(widget.helpRequest.sender_id, DataBaseService().userInNeedCollection),
Text(widget.helpRequest.description),
]
)
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
onTap: (){
_launchCaller();
** Here i think I should add the code **
},
onLongPress: () => print("Long Press: Call"),
child: Padding(
padding: EdgeInsets.all(16.0),
child: Icon(Icons.call,
size: 20.0,
color: Colors.green,
)
),
),
],
),
),
),
);
And this setState function I tried to use which didn't work well in my case (I was changing state on the onTap function):
void initState() {
super.initState();
color = Colors.transparent;
}
Final Output:
You can set the color of a specific card, but for that to happen, you need to have some way to reference that the selected card was clicked on, with this reference we can decide whether the card is selected and if yes then change the color according to our preference.
In the following example, I am more or less using the same card widget template that you stated in the question, then I am using the ListView.builder to render five cards, each having the same functionality.
Whenever the call button is pressed, the corresponding index of that specific card is assigned to the state selectedIndex and from this, we can assign the color to the selected Card.
Here is the full example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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 selectedIndex;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Card(
margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
color: index == selectedIndex
? Colors.amberAccent
: Colors.brown[30],
child: ListTile(
isThreeLine: true,
title: Row(children: <Widget>[
Container(
child: Text("Some Text"),
alignment: Alignment.topLeft,
),
Spacer(),
Container(
child: Text("Some Text"),
alignment: Alignment.topRight,
),
]),
subtitle: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Some Text"),
])),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
},
onLongPress: () => print("Long Press: Call"),
child: Padding(
padding: EdgeInsets.all(16.0),
child: Icon(
Icons.call,
size: 20.0,
color: Colors.green,
),
),
),
],
),
),
),
);
},
),
),
);
}
}
Keep track of the clicked item and pass the index of the list
int clickedItemPosition = -1;
bool isChecked(currentPosition) => clickedItemPosition == currentPosition;
Then in your card
//..
Card(
margin: EdgeInsets.fromLTRB(20.0, 6.0, 20.0, 0.0),
color: isChecked(index) ? Colors.blue : Colors.transparent,
//..
In Gesture detector update the clickedItemPosition
//...
GestureDetector(
onTap: () => setState(() => clickedItemPosition = index),
//..
Related
When using the cupertino sliver navigation bar, the body scrolls under it.
I tried using nested scroll view but even that resulted in the same behavior
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
#override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: CustomScrollView(
slivers: [
CupertinoSliverNavigationBar(
brightness: Brightness.dark,
padding: EdgeInsetsDirectional.zero,
largeTitle: Text(
'Tasks',
style: appBarTextStyle,
),
trailing: CupertinoButton(
onPressed: () {}, child: primaryIcon(Icons.search)),
leading: CupertinoButton(
onPressed: () {}, child: primaryIcon(Icons.menu)),
),
SliverFillRemaining(
child: Container(
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 15.0)
: const EdgeInsets.symmetric(
horizontal: 35.0, vertical: 15.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
filteredWidget(
context,
'Scheduled',
'No scheduled tasks',
arrayController.scheduledTodos,
Icons.schedule),
filteredWidget(
context,
'Today',
'Schedule a task for today',
arrayController.todayTodos,
Icons.calendar_today),
],
),
const SizedBox(
height: 20.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
filteredWidget(
context,
'Completed',
'No completed tasks',
arrayController.doneTodos,
Icons.done_rounded),
filteredWidget(context, 'All', 'No tasks yet',
arrayController.allTodos, Icons.task)
],
),
],
)),
)
],
),
);
}
}
Here is the full code on Github
Reproducible example:
import 'package:flutter/cupertino.dart';
void main() => runApp(const SliverNavBarApp());
class SliverNavBarApp extends StatelessWidget {
const SliverNavBarApp({super.key});
#override
Widget build(BuildContext context) {
return const CupertinoApp(
theme: CupertinoThemeData(brightness: Brightness.light),
home: SliverNavBarExample(),
);
}
}
class SliverNavBarExample extends StatelessWidget {
const SliverNavBarExample({super.key});
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
// A ScrollView that creates custom scroll effects using slivers.
child: CustomScrollView(
// A list of sliver widgets.
slivers: <Widget>[
const CupertinoSliverNavigationBar(
leading: Icon(CupertinoIcons.person_2),
// This title is visible in both collapsed and expanded states.
// When the "middle" parameter is omitted, the widget provided
// in the "largeTitle" parameter is used instead in the collapsed state.
largeTitle: Text('Contacts'),
trailing: Icon(CupertinoIcons.add_circled),
),
// This widget fills the remaining space in the viewport.
// Drag the scrollable area to collapse the CupertinoSliverNavigationBar.
SliverFillRemaining(
child: Container(
height: 100.0,
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 15.0)
: const EdgeInsets.symmetric(
horizontal: 35.0, vertical: 15.0),
),
),
],
),
);
}
}
What i want to achieve is to have my listview update automatically when the addtoBucketlist fucntion inside the createbucketlistpage is called. Currently the first instance works but when i proceed to add another the listview doesnt update and i have to navigate away from the bucketlistpage and back before i can see the changes.
I am generating a list of widgets on the createbucketlistpage where users can click on a particular widget and it should get added to the bucket list page automatically.
Heres my provider code
class UpcomingTour extends ChangeNotifier {
List<String> upcomingTourImages = [
'assets/UpcomingToursImages/ut1.jpg',
'assets/UpcomingToursImages/ut2.jpg',
'assets/UpcomingToursImages/ut3.jpg',
];
List<String> upComingTourTitle = [
'Kruger',
'Camping and Hiking Ilorin',
'Labadi Beach',
];
List<String> upcomingTourDate = [
'Loading...',
'Loading...',
'Loading...',
];
List<Widget> upcomingTourBody = [
//kruger
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''The big five: Elephant, Lion, Rhino, Leopard and Buffalo. Kruger park is home to a stunning diversity of wildlife, trees, flowers and most importantly The big five. Steeped in legend and history with exquisite accomodations and meals. Kruger national park is one of the most famous tourist destinations in the world. To get the real taste of a safari tour, book a tour to Kruger national park.'''),
),
//camping
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''Enjoy one of the best outdoor activities in the world in the beautiful city of Ilorin.
Our world has changed drastically since the pandemic and many people are feeling more isolated. Work from home has become the norm for many and social media, email and the general 24/7 noise of life can leave us more stressed out.
Disconnecting from technology and reconnecting with nature gives our brain a much-needed recharge, especially among those with sedentary occupations. Camping and Hiking helps you reconnect with nature, build better relationships and aids in overall physical and mental fitness'''),
),
// labadi beach
const Padding(
padding: EdgeInsets.all(16.0),
child: Text(
'''Also known as La pleasure beach. Labadi beach is one of the best beaches on the Ghana coast, filled with lots and lots of fun activities such as games, horse riding and most importantly, relaxing. Labadi beach is perfect for both couple and family vacations.'''),
),
];
var newUpcomingTour;
List<Padding> upcomingTourList = [];
// The original function to generate a list of Upcoming tour widgets
List<Padding> getUpcomingTour(BuildContext context) {
for (int i = 0; i < upcomingTourImages.length; i++) {
String imagePath = upcomingTourImages[i];
String title = upComingTourTitle[i];
String desc = upcomingTourDate[i];
newUpcomingTour = Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.grey,
),
borderRadius: const BorderRadius.all(Radius.circular(10))),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
topLeft: Radius.circular(10)),
child: Image.asset(
imagePath,
fit: BoxFit.cover,
width: 100,
height: 100,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
style: const TextStyle(
fontSize: 17,
),
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(desc),
),
],
),
],
),
),
Flexible(
child: GestureDetector(
onTap: () {
Provider.of<UpcomingTour>(context, listen: false)
.addToBucketList(upcomingTourList[i]);
Navigator.pop(context);
},
child: const Padding(
padding: EdgeInsets.only(right: 5),
child: CircleAvatar(
child: Icon(Icons.add),
),
)),
),
],
),
),
);
upcomingTourList.add(newUpcomingTour);
}
return upcomingTourList;
}
List<Padding> bucketList = [];
void addToBucketList(Padding item) {
bucketList.add(item);
notifyListeners();
}
void removeFromBucketList(Padding item) {
bucketList.remove(item);
notifyListeners();
}
void clearBucketList() {
bucketList.clear();
notifyListeners();
}
}
The code above contains the function that generates the list of widgets that are displayed in the create bucket list page
Heres the code for my create bucketlist page
class _BucketListPageContentsState extends State<BucketListPageContents> {
String searchInput = '';
TextEditingController controller = TextEditingController();
UpcomingTour upcomingTour = UpcomingTour();
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Create your explore bucket list',
style: kBoldTextStyleBlack,
),
addVerticalSpacing(20),
const Text(
'Let’s make your dreams come true. Tell us where you would like to visit or an event you would like to attend and we might just make it happen'),
addVerticalSpacing(20),
const Text(
'Add from upcoming tours',
style: kBoldTextStyleBlack,
),
addVerticalSpacing(20),
],
),
Flexible(
child: ListView(
children: upcomingTour.getUpcomingTour(context),
),
),
],
),
);
}
}
And finLally heres the bucketlistpage
class BucketList extends StatefulWidget {
static const String id = 'Bucketlistpage';
const BucketList({Key? key}) : super(key: key);
#override
State<BucketList> createState() => _BucketListState();
}
class _BucketListState extends State<BucketList> {
int? bucketListlength;
UpcomingTour upcomingTour = UpcomingTour();
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
bucketListlength = Provider.of<UpcomingTour>(context).bucketList.length;
if (bucketListlength == 0) {
return SafeArea(
child: Scaffold(
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
tooltip: 'Add new tour to Bucketlist',
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
PageTransition(
child: const CreateBucketListPage(),
type: PageTransitionType.rightToLeft));
}),
body: Align(
alignment: Alignment.center,
child: ListView(scrollDirection: Axis.vertical, children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.41,
decoration: const BoxDecoration(
color: Colors.grey,
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage('assets/emptybucketlist.jpg'),
),
),
),
),
const Align(
alignment: Alignment.center,
child: Text(
'BucketList is Empty',
style: kBoldTextStyleBlack,
),
),
addVerticalSpacing(20),
const Padding(
padding: EdgeInsets.all(10.0),
child: Text(
'Let’s make your dreams come true. Add places/events you woud like to visit/attend. '),
),
]),
),
),
);
} else {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.black),
onPressed: () => Navigator.of(context).pop(),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue,
tooltip: 'Add new tour to BucketList',
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
PageTransition(
child: const CreateBucketListPage(),
type: PageTransitionType.rightToLeft))
.then((value) => setState(() {}));
}),
body: Column(
children: [
Flexible(
child: ListView(
shrinkWrap: true,
key: const Key('upcomingTour'),
children: Provider.of<UpcomingTour>(context, listen: true)
.bucketList,
),
),
ReusableButton(const Text('Clear BucketList'), () {
Provider.of<UpcomingTour>(context, listen: false)
.clearBucketList();
})
],
),
),
);
}
}
}
i cannot use listview.builder because my widgets are already being generated by a fucntion and i dont need to build them as they are being added manually by the user.
Heres my idea is to listen for the changes in the length of the bucketlist list in the provider model and use it to show an empty bucketlist page if the length of the bucketlist list is 0 or show the list.
I am new to provider and ihave tried different things from tutorials but none worked. I even changed from Change notifier to value notifier but it didnt work.
I have also tried different ways to refresh the bucketlist page when i pop from createbucketlistpage but that too doesnt work.
Any help is greatly appreciated.
use Consumer in your page whenever your Data Changes your widget will rebuild automatically (when using notifyListiners method ), also its a performance wise to use Consumer which is rebuild only the widget inside the Consumer not the whole page and that will provide a better performance for your app
here is an example:
Consumer<yourProviderType>(
builder:(context, value, child) => value.yourList.isEmpty
? CircularProgressIndicator():
build a widget which contain data comes from the value instnace
)
I want to remove the following blue padding from MaterialBanner widget, but it doesn't seem to be customizable. I want to insert an image in the red region.
I looked into MaterialBanner for using across Scaffold widgets because ScaffoldMessenger doesn't allow me to insert widgets other than MaterialBanner.
Is there any suggestion?
dartpad.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Scaffold(body: JustBanner())));
}
class JustBanner extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _JustBannerState();
}
}
class _JustBannerState extends State<JustBanner> {
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
final messenger = ScaffoldMessenger.of(context);
messenger.clearMaterialBanners();
messenger.showMaterialBanner(MaterialBanner(
padding: EdgeInsets.zero,
leadingPadding: EdgeInsets.zero,
leading: const SizedBox.shrink(),
backgroundColor: Colors.blue,
content: Container(
color: Colors.red,
width: 200,
height: 50,
),
actions: const [SizedBox.shrink()]));
},
child: const Text('Banner')),
],
);
}
}
Container(
width: MediaQuery.of(context).size.width,
child: MaterialBanner(
content: Text('Hello'),
actions: [
Icon(Icons.add),
],
),
),
Its no possible without copy and re-create the class, buttonBar always appear:
final Widget buttonBar = Container( // <-- problematic widget
alignment: AlignmentDirectional.centerEnd,
constraints: const BoxConstraints(minHeight: 52.0),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: OverflowBar(
overflowAlignment: widget.overflowAlignment,
spacing: 8,
children: widget.actions,
),
);
final double elevation = widget.elevation ?? bannerTheme.elevation ?? 0.0;
final Color backgroundColor = widget.backgroundColor
?? bannerTheme.backgroundColor
?? theme.colorScheme.surface;
final TextStyle? textStyle = widget.contentTextStyle
?? bannerTheme.contentTextStyle
?? theme.textTheme.bodyText2;
Widget materialBanner = Container(
margin: EdgeInsets.only(bottom: elevation > 0 ? 10.0 : 0.0),
child: Material(
elevation: elevation,
color: backgroundColor,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: padding,
child: Row(
children: <Widget>[
if (widget.leading != null)
Padding(
padding: leadingPadding,
child: widget.leading,
),
Expanded(
child: DefaultTextStyle(
style: textStyle!,
child: widget.content,
),
),
if (isSingleRow)
buttonBar, // <----- here
],
),
),
if (!isSingleRow)
buttonBar, // <----- here
if (elevation == 0)
const Divider(height: 0),
],
),
),
);
I have two images in flutter which is use for pin and unpin the data in list view so my program is that when i click on pin image than unpin image should be hide and when i click unpin image than pin image should hide. so how to implement this thing in flutter.
here is my demo code
class PinUnpinData extends StatefulWidget {
#override
_PinUnpinDataState createState() => _PinUnpinDataState();
}
class _PinUnpinDataState extends State<PinUnpinData> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
"Pin-Unpin",
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
backgroundColor: Colors.white,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
InkWell(
onTap: () {
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Image.asset(
"assets/images/pin.png",
height: 20,
width: 20,
),
)),
InkWell(
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(20),
child: Image.asset(
"assets/images/unpin.png",
height: 20,
width: 20,
),
))
],
),
),
);
}
}
Create a local variable to keep the track of pinned state. Then update the state of that variable on tap of the button using the setState() method. Also, for showing the relevant image, just check the value of thepinned variable and show the relevant image, if it's true then show the unpin image else pin image.
class _PinUnpinDataState extends State<PinUnpinData> {
bool pinned = false; // to keep track if it's pinned or not
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Text(
"Pin-Unpin",
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
backgroundColor: Colors.white,
body: Center(
child: InkWell(
onTap: () {
setState(() {
pinned = pinned ? false : true; // check if pinned is true, if its true then set it false and voice versa
});
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Image.asset(
pinned
? "assets/images/unpin.png" //show this image when it's pinned
: "assets/images/pin.png", // show this image when it not pinned
height: 20,
width: 20,
),
)),
),
);
}
}
I want to place a CircularProgressIndicator inside a RaisedButton, but when I do it, the CircularProgressIndicator doesn't fit the button and sticks out of it.
I want to shrink the CircularProgressIndicator, so that it fits inside the RaisedButton, I know I can do it using a SizedBox but I want it to be automatic, not that I give it the size that I want. I tried FittedBox but all its options make no difference at all.
this is my button's code:
RaisedButton(
onPressed: () => print('hi'),
shape: StadiumBorder(),
color: Theme.of(context).primaryColor,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 12, top: 6, bottom: 6),
child: CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
),
Text('Loading'),
],
),
),
And this is how it looks like:
When I Add Padding it grows the button:
Are there any ways to achieve this automatically?
EDIT:
Just to make it clear, the end effect that I want is this:
Before and after the user presses the button:
I want the height to stay the same, automatically, without magic values inside a SizedBox.
This is totally possible. You wrap the CircularProgressIndicator inside a SizedBox to constrain it to that size. Also using MainAxisSize.Min in the Row prevents the button from trying to expand to infinite size.
ElevatedButton(
onPressed: (){},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue)),
child: isLoading
? Padding(
padding: const EdgeInsets.all(5.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("Loading"),
SizedBox(
width: 5,
),
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.white),
backgroundColor: Colors.blue,
strokeWidth: 3,
),
)
],
),
)
: Text("Not Loading"))
This gives you the following
Looking at the CircularProgressIndicator source code, I can find there's a hardcoded minimum size of 36.0 height/width, so there's no way of getting smaller than that automatically.
I re-created your case and was able to see the CircularProgressIndicator properly inside the RaisedButton maintaining it's size.
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Progress Button',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Progress Button'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => print('hi'),
shape: StadiumBorder(),
color: Theme.of(context).primaryColor,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 12, top: 6, bottom: 6),
child: CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
),
Text('Loading'),
],
),
),
],
),
),
);
}
}
Output (both platforms):
you can use mainAxisAlignment: MainAxisAlignment.spaceBetween to make space between your widgets in Row.
You can wrap your Row widget to a Padding widget to make padding for whole content in your button as well.
Padding(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CircularProgressIndicator(
backgroundColor: Colors.white,
strokeWidth: 2,
),
Text('Loading'),
],
),
)