Flutter DataTable cell text not wrapping inside of a row - flutter

I'm trying to put a dataTable in a row widget, but I lose the text wrapping in the individual cells when I do that.
This code works correctly:
class Testing extends StatefulWidget {
Testing({Key key}) : super(key: key);
#override
_MyDataTableState createState() => _MyDataTableState();
}
class _MyDataTableState extends State<Testing> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DataTable"),
),
body: SingleChildScrollView( <-- Text wrapping goes away when wrapped in a Row()
padding: const EdgeInsets.all(8.0),
child: DataTable(
// columnSpacing: 100,
columns: [
DataColumn(
label: Container(
width: 100,
child: Text('Item Code'),
),
),
DataColumn(
label: Text('Stock Item'),
),
],
rows: [
DataRow(
cells: [
DataCell(
Text('Yup. text.'),
),
DataCell(
Text(
'This is a really long text. It\'s supposed to be long so that I can figure out what in the hell is happening to the ability to have the text wrap in this datacell. So far, I haven\'t been able to figure it out.'),
)
],
),
],
),
),
);
}
}
Is there a way to preserve the text wrapping inherent in a DataTable when wrapped in a row?

Wrap SingleChildScrollView with Expanded too.
class Testing extends StatefulWidget {
Testing({Key key}) : super(key: key);
#override
_MyDataTableState createState() => _MyDataTableState();
}
class _MyDataTableState extends State<Testing> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DataTable"),
),
body: Row(
children: <Widget>[
//TODO: use Expanded here
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: DataTable(
// columnSpacing: 100,
columns: [
DataColumn(
label: Container(
width: 100,
child: Text('Item Code'),
),
),
DataColumn(
label: Text('Stock Item'),
),
],
rows: [
DataRow(
cells: [
DataCell(
Text('Yup. text.'),
),
DataCell(
Text(
'This is a really long text. It\'s supposed to be long so that I can figure out what in the hell is happening to the ability to have the text wrap in this datacell. So far, I haven\'t been able to figure it out.'),
)
],
),
],
),
),
),
],
),
);
}
}

I have been stuck on a similar problem since this morning.
For me, the DataColumn "label" Text wasn't wrapping. Although the text in the DataRow cell was wrapping just fine
After trying multiple variations, this is what worked for me.
Put the label text inside Expanded and set softWrap text property to true
DataTable(
columns: [
DataColumn(
label: Expanded(
child: Text(
"Very long text goes here",
softWrap: true,
),
)),
],
rows: [],
);

I guess it happens because of how Row naturally works. It takes as much as available horizontal axis. Although the mainAxisSize can be configured(min or max) according to the children, DataTable doesn't have initial width therefore resulting Text not being wrapped.
For quick fix you can wrap the Data Table in Container and give it a const width.
Quick test on DartPad: https://dartpad.dev/a664a29160b6e0443e9ca3bf28d5ec69
Snippet:
class Testing extends StatefulWidget {
Testing({Key key}) : super(key: key);
#override
_MyDataTableState createState() => _MyDataTableState();
}
class _MyDataTableState extends State<Testing> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("DataTable"),
),
body: Row(
children: [
Container(
width: 300, // Give the DataTable a const width.
child: DataTable(
columns: [
DataColumn(
label: Container(
width: 100,
child: Text('Item Code'),
),
),
DataColumn(
label: Text('Stock Item'),
),
],
rows: [
DataRow(
cells: [
DataCell(
Text('Yup. text.'),
),
DataCell(
Wrap(
children: [
Text(
'This is a really long text. It\'s supposed to be long so that I can figure out what in the hell is happening to the ability to have the text wrap in this datacell. So far, I haven\'t been able to figure it out.')
],
),
)
],
),
],
),
)
],
),
);
}
}

Related

how to dynamically build and display widgets based off a string value?

I've created an app with a large amount of buttons that update a string value.
there is a container in the middle that needs to display different widgets for each button chosen. and when a new button is pressed, the middle container will need to pop the old displaying widgets and build a new one in it's place.
I thought about layering the middle container with all the widgets to display and use the visibility widget connected to the string value using if statements to show/hide them but I don't think this is the best way to do it.
I've put together sample code below
class TestPage extends StatefulWidget {
const TestPage({Key? key}) : super(key: key);
#override
State<TestPage> createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
String displayText = 'Choose option';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: Column(
children: [
SizedBox(
height: 50,
),
Button(
text: 'option A',
onTap: () {
displayText = 'Option A';
},
),
//what would I put in the onTap property to get the app to build the OneOfTheWidgetsToBuild widget in the Display Container below based off the updated string value?
SizedBox(
height: 5,
),
Button(text: 'option B', onTap: null),
],
),
),
Expanded(
child: Column(
children: [
SizedBox(
height: 50,
),
Center(
child: Container(
//DISPLAY CONTAINER
height: 40,
width: 100,
color: Colors.yellow,
child: Center(
child: Text(
'$displayText',
style: TextStyle(color: Colors.black),
),
),
),
)
],
),
),
Expanded(
child: Column(
children: [
SizedBox(
height: 50,
),
Button(text: 'option C', onTap: null),
SizedBox(
height: 5,
),
Button(text: 'option D', onTap: null),
],
),
),
],
),
);
}
}
and here is the widget to display in the middle box:
class OneOfTheWidgetsToBuild extends StatelessWidget {
const OneOfTheWidgetsToBuild({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 40,
width: 100,
color: Colors.green,
);
}
}
thanks so much for your help
If I understand you correctly: You want to show a bunch of different widgets based on the string's value.
One way to go about it, instead of using a bunch of if/else statements is to use a switch case and a function to correctly render the correct widgets.
For example:
Create a widget:
var _displayWidget = Container();
then, create a function to update that widget
void updateWidget(String option) {
switch (option) {
case 'Option A':
_displayWidget = widgetOne();
break;
case 'Option B':
_displayWidget = widgetTwo();
break;
default:
_displayWidget = Container();
break;
}
}
and then, in your Build method:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
body: Column(
children: [
DropdownButton<String>(
items: const [
DropdownMenuItem(
child: Text('Option A'),
value: 'Option A',
),
DropdownMenuItem(
child: Text('Option B'),
value: 'Option B',
),
],
onChanged: (value) {
setState(() {
updateWidget(value!);
});
},
),
_displayWidget,
],
),
);
}

I want the text and icon together in a line under one widget in body

I want the text and icon together in a line under one widget in body. I tried and I got the following error
Too many positional arguments, 0 expected but 1 received
Can Any one help me with this? Thanks in advance!
void main() => runApp(const Myapp());
class Myapp extends StatefulWidget {
const myapp({Key? key}) : super(key: key);
_MyappState createState() => _MyappState();
}
class _MyappState extends State<Myapp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Padding(
padding: EdgeInsets.only(top: 30),
child: Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
/** Positioned WIdget **/
Positioned(
top: 0.0,
child: Image.asset(
'images/logo.png',
height: 150,
width: 150,
),
),
//Positioned
/** Positioned WIdget **/
//Positioned
], //<Widget>[]
), //Stack
),
Text.rich(
TextSpan(
children: [
TextSpan(text: 'Click'),
WidgetSpan(child: Icon(Icons.add)),
TextSpan(text: 'to add'),
],
),
)
),
);
}
}
First, the error "Too many positional arguments, 0 expected but 1 received", is due to the fact that the placement of RichText is outside the Stack Widget children. (The square brackets [].)
To achieve the desired output as you mentioned in the question, use a row specified in code comments with the attached code.
void main() => runApp(const Myapp());
class Myapp extends StatefulWidget {
const Myapp({Key? key}) : super(key: key);
#override
_MyappState createState() => _MyappState();
}
class _MyappState extends State<Myapp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Padding(
padding: const EdgeInsets.only(top: 30),
child: Stack(
alignment: AlignmentDirectional.center,
children: const <Widget>[
/** Positioned WIdget **/
// Positioned(
// top: 0.0,
// child: Image.asset(
// 'images/logo.png',
// height: 150,
// width: 150,
// ),
// ),
/** Positioned WIdget **/
/// This is where the RichTextWidget should be
Text.rich(
TextSpan(
children: [
TextSpan(text: 'Click'),
WidgetSpan(child: Icon(Icons.add)),
TextSpan(text: 'to add'),
],
),
),
/// Additionally, use a Row([Text,Icon,Text]) and use
/// crossAxisAlignment Property to align the children
], //<Widget>[]
), //Stack
),
/// You placed the below RickText outside the Stack, it should be above in the Widget[] of the stack.
// Text.rich(
// TextSpan(
// children: [
// TextSpan(text: 'Click'),
// WidgetSpan(child: Icon(Icons.add)),
// TextSpan(text: 'to add'),
// ],
// ),
// )
),
);
}
}
If you want to place a button with title and icon then you can use TextButton.icon instead of putting it inside a row or putting inside RichText.
Here's the button example:
class DemoWidget extends StatelessWidget {
const DemoWidget({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.mail),
label: const Text("Title"),
),
),
);
}
}
Then you can put it inside a stack and assign a background image in it.
You can use Positioned.fill and then try modifying the parameters until it matches your requirment.
caffold(
body: Center(
child: Container(color: Colors.lightGreen,
height: 200,
width: 200,
child: Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
Positioned(child: Image.asset("assets/test.png")),
const Text.rich(
TextSpan(
children: [
TextSpan(text: 'Click'),
WidgetSpan(child: Icon(Icons.add)),
TextSpan(text: 'to add'),
],
),
),
], //<Widget>[]
),
),
),
)

Flutter scrollable layout with dynamic child

I want to create a generic Layout which accepts a child Widget as a parameter, that lays out the content as follows:
I have an AppBar at the Top, a Title (headline), and below that the Content (could be anything). At the bottom, I have a Column with a few buttons. If the content is too big for the screen, all those widgets, except the AppBar, are scrollable. If the content fits the screen, the title and content should be aligned at the top, and the buttons at the bottom.
To showcase what I mean, I created a drawing:
It is easy to create to scrollable content functionality. But I struggle with laying out the content so that the buttons are aligned at the bottom, if the content does NOT need to be scrollable.
It is important to say that I don't know the height of the content widget or the buttons. They are dynamic and can change their height. Also, the title is optional and can have two different sizes.
What I tried is the following:
import 'package:flutter/material.dart';
class BaseScreen extends StatelessWidget {
final String? title;
final bool bigHeader;
final Widget child;
final Widget bottomButtons;
const BaseScreen({
Key? key,
required this.child,
required this.bottomButtons,
this.bigHeader = true,
this.title,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final AppBar appBar = AppBar(
title: Text("AppBar"),
);
double minChildHeight = MediaQuery.of(context).size.height -
MediaQuery.of(context).viewInsets.bottom -
MediaQuery.of(context).viewInsets.top -
MediaQuery.of(context).viewPadding.bottom -
MediaQuery.of(context).viewPadding.top -
appBar.preferredSize.height;
if (title != null) {
minChildHeight -= 20;
if (bigHeader) {
minChildHeight -= bigHeaderStyle.fontSize!;
} else {
minChildHeight -= smallHeaderStyle.fontSize!;
}
}
final Widget content = Column(
mainAxisSize: MainAxisSize.min,
children: [
if (title != null)
Text(
title!,
style: bigHeader ? bigHeaderStyle : smallHeaderStyle,
textAlign: TextAlign.center,
),
if (title != null)
const SizedBox(
height: 20,
),
ConstrainedBox(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
child,
bottomButtons,
],
),
constraints: BoxConstraints(
minHeight: minChildHeight,
),
),
],
);
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: content,
),
);
}
TextStyle get bigHeaderStyle {
return TextStyle(fontSize: 20);
}
TextStyle get smallHeaderStyle {
return TextStyle(fontSize: 16);
}
}
The scrolling effects work perfectly, but the Buttons are not aligned at the bottom. Instead, they are aligned directly below the content. Does anyone know how I can fix this?
DartPad you can check here
customscrollview tutorial
Scaffold(
// bottomNavigationBar: ,
appBar: AppBar(
title: Text(" App Bar title ${widgets.length}"),
),
//============
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
// controller: _mycontroller,
children: [
title,
...contents,
// ---------------------This give Expansion and button get down --------
Expanded(
child: Container(),
),
// ---------------------This give Expansion and button get down --------
Buttons
],
),
)
],
))
We can Achieve with the help of CustomScrollView widget and Expanded widget.here Expanded widget just expand between the widget
Sample Code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),
);
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var widgets = [];
var _mycontroller = ScrollController();
#override
Widget build(BuildContext context) {
var title = Center(
child: Text(
"Scrollable title ${widgets.length}",
style: TextStyle(fontSize: 30),
));
var contents = [
...widgets,
];
var Buttons = Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {
setState(() {
widgets.add(Container(
height: 100,
child: ListTile(
title: Text(widgets.length.toString()),
subtitle: Text("Contents BTN1"),
),
));
});
// _mycontroller.jumpTo(widgets.length * 100);
},
child: Text("BTN1"),
),
),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {
setState(() {
if (widgets.length > 0) {
widgets.removeLast();
}
});
// _mycontroller.jumpTo(widgets.length * 100);
},
child: Text("BTN2"),
),
),
))
],
);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
// bottomNavigationBar: ,
appBar: AppBar(
title: Text(" App Bar title ${widgets.length}"),
),
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
// controller: _mycontroller,
children: [
title,
...contents,
Expanded(
child: Container(),
),
Buttons
],
),
)
],
)),
),
);
}
}
Try this:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: BaseScreen(
bottomButtons: [
ElevatedButton(onPressed: () {}, child: const Text('Button 1')),
ElevatedButton(onPressed: () {}, child: const Text('Button 2')),
],
content: Container(
color: Colors.lightGreen,
height: 200,
),
title: 'Title',
),
);
}
}
class BaseScreen extends StatelessWidget {
final bool bigHeader;
final List<Widget> bottomButtons;
final String? title;
final Widget content;
const BaseScreen({
this.bigHeader = true,
required this.bottomButtons,
required this.content,
this.title,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AppBar'),
),
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
children: [
if (title != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
title!,
style: bigHeader ? _bigHeaderStyle : _smallHeaderStyle,
textAlign: TextAlign.center,
),
),
content,
const Spacer(),
...bottomButtons,
],
),
),
],
),
);
}
TextStyle get _bigHeaderStyle => const TextStyle(fontSize: 20);
TextStyle get _smallHeaderStyle => const TextStyle(fontSize: 16);
}
Screenshots:
without_scrolling
scrolled_up
scrolled_down

How to not load the children of a ExpansionTile

I have the follow structure: A list of Expansion tiles > clicking on it, opens another list of ExpansionTiles > Clicking in one of them, it should open some widgets according to a SQL query.
The problem is, when I tap in the first Expansion Tile it loads all the widgets from all the Expansion Tiles inside the first option making the query very slow. I want to only load the widgets when I tap in the second one (loading only the necessary ones)
Here is the code:
1st list:
class ListItemsScreen extends StatefulWidget {
#override
_ListItemsScreenState createState() => _ListItemsScreenState();
}
class _ListItemsScreenState extends State<ListItemsScreen> {
final Widget appBar = AppBar(
title: Text('ITEMS'),
actions: [
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.shopping_bag_outlined),
onPressed: () {
Navigator.of(context)
.pushNamed(ROUTE_CHART);
},
),
),
],
);
#override
Widget build(BuildContext context) {
final List items = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: appBar,
body: items == null || items.isEmpty ?
Center(child: Text("0 items here"),)
:
ListView(
children: [
...items.map<Widget>(
(item) {
return ExpansionTile(
leading: Image.asset(ASSET_IMAGE,
fit: BoxFit.cover
),
title: Text('${item.code} | ${item.description}'),
subtitle:
Text('${item.color}'),
children: [
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: ProductWidget(item),
),
],
),
);
},
)
],
)
);
2nd list (ProductWidget):
class ProductWidget extends StatefulWidget {
final Product product;
ProductWidget(this.produto);
#override
_ProductWidgetState createState() => _ProductWidgetState();
}
class _ProdutoGradeWidgetState extends State<ProdutoGradeWidget> {
#override
Widget build(BuildContext context) {
CustomScrollView(
slivers: [
StreamBuilder(
stream: product.stream,
builder: (ctx, snapshot) {
return SliverList(
delegate: SliverChildBuilderDelegate((ctx, i) {
if (i == 0) {
return Column(
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
'I HAVE THIS PRODUCT IN THESE COLORS',
style: TextStyle(
fontSize: 20,
color:
Theme.of(context).textTheme.caption.color,
),
)
),
),
const SizedBox(height: 20.0),
ProductColorsWidget(color: snapshot.data[i]),
],
);
} else if (i == snapshot.data.length - 1) {
return Column(
children: [
ProductColorsWidget(color: snapshot.data[i]),
const SizedBox(height: 20.0),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
'Qtd',
style: TextStyle(
fontSize: 16,
color:
Theme.of(context).textTheme.caption.color,
),
),
),
),
const SizedBox(height: 20.0),
],
);
}
return ProductColorsWidget(color: snapshot.data[i]);
}, childCount: snapshot.data.length),
);
}
},
),
],
);
}
}
}
}
3rd part (Product Colors Widget where I list the second Expansion Tiles):
class ProductColorsWidget extends StatelessWidget {
final ColorProduct color;
ProdutoCorGradeWidget({this.color});
#override
Widget build(BuildContext context) {
return ExpansionTile(
maintainState: true,
tilePadding: EdgeInsets.all(15.0),
title: Text(
'${color.id} - ${color.description}',
style: Theme.of(context)
.textTheme
.subtitle1
.copyWith(fontWeight: FontWeight.w600),
),
childrenPadding: EdgeInsets.all(10.0),
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...color.sizes.map<Widget>(
(item) {
return Column(
children: [
Expanded(
child: Text(
item.description, textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
)
),
...item.prices.map<Widget>((size) {
return PricesWidget( //here it should show the widgets according to the second ExpansionTiles
color: color,
size: size
);
})
]
);
}
)
],
)
)
],
);
}
}
So, to be clear, what I want is: First It lists the products (with expansionTiles), expanding one it should show the second Tiles (with sizes) and after selecting one it should show the widgets.
..But what is happening now is: List the products and when I select one the app loads all the widget from all the second 'expansionTiles' making it slow to show the second list. What should I do?
I think the problem is with
ExpansionTile(
maintainState: true,
.....
)
I had a similar issue in which I had an ExpansionTile that was its own child and it caused a stack overflow because it was building them all. After setting maintainState to false the problem was solved.
You might have to adapt your state management according to that the children state may not be saved

How to change CupertinoSwitch size in flutter?

I want to change the size of the CupertinoSwitch in flutter. I have tried putting the switch in Container but changing the size of the container does not affect the switch.
You can copy paste run full code below
You can use Transform.scale and set scale, 1 means normal size, 0.8 means smaller size
code snippet
Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
value: _switchValue,
onChanged: (bool value) {
setState(() {
_switchValue = value;
});
},
),
)
working demo
full code
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.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: CupertinoSwitchDemo(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class CupertinoSwitchDemo extends StatefulWidget {
static const String routeName = '/cupertino/switch';
#override
_CupertinoSwitchDemoState createState() => _CupertinoSwitchDemoState();
}
class _CupertinoSwitchDemoState extends State<CupertinoSwitchDemo> {
bool _switchValue = false;
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: const Text('Switch'),
// We're specifying a back label here because the previous page is a
// Material page. CupertinoPageRoutes could auto-populate these back
// labels.
previousPageTitle: 'Cupertino',
//trailing: CupertinoDemoDocumentationButton(CupertinoSwitchDemo.routeName),
),
child: DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Semantics(
container: true,
child: Column(
children: <Widget>[
Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
value: _switchValue,
onChanged: (bool value) {
setState(() {
_switchValue = value;
});
},
),
),
Text(
"Enabled - ${_switchValue ? "On" : "Off"}"
),
],
),
),
Semantics(
container: true,
child: Column(
children: const <Widget>[
CupertinoSwitch(
value: true,
onChanged: null,
),
Text(
'Disabled - On'
),
],
),
),
Semantics(
container: true,
child: Column(
children: const <Widget>[
CupertinoSwitch(
value: false,
onChanged: null,
),
Text(
'Disabled - Off'
),
],
),
),
],
),
),
),
),
);
}
}
The accepted answer solves almost everything you need. But keep in mind that, if you make your widget smaller with Transform.scale you still have the same invisible space that the widget had before scaling it. That means: even if you scale your Switch, it still occupies the original size. A workaround for that is just to wrap it with a container and give it a desired width and height.
Note: After scaling your Switch, in order to not apply the transformation when performing hit tests, set transformHitTests to false. That way you can more easily control the area where you can tap or click.
Container(
color: Colors.red,
height: 30, //set desired REAL HEIGHT
width: 35, //set desired REAL WIDTH
child: Transform.scale(
transformHitTests: false,
scale: .5,
child: CupertinoSwitch(
value: switchValue,
onChanged: (value) {
setState(() {
switchValue = value;
});
},
activeColor: Colors.green,
),
),
),
using Transform.scale is really a good way of doing it but can cause you some trouble designing or arranging widgets on the screen.
So instead, you can wrap your CupertinoSwitch in FittedBox which is inside another Container, giving you more control over your widget.
You can copy-paste the below code,
you only need to set height and width and make your FittedBox to BoxFit.contain.
Container(
height: 200.0,
width: 200.0,
child: FittedBox(
fit: BoxFit.contain,
child: CupertinoSwitch(
value: _switchValue,
onChanged: (value) {
setState(() {
_switchValue = value;
});
},
),
),
),