I am new to flutter . I am try to design a pdf file that have a long table. but table don't split automatically . if the table longer than an a4 size the pdf file will not generate. is there a simple solution for that I am missing?
if the counter is longer than 25 the pdf will not generate.
yes I know for one table it easy to use if/else function and generate two tables but in my main project I have multiple tables that have different columns numbers. so I need a solution that make the tables split automatically.
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart';
import 'package:printing/printing.dart';
import 'dart:math' as math;
Future<void> testPdf() async {
final Document pdf = Document();
var myFont = Font.ttf(await rootBundle.load("assets/fonts/HacenTunisia.ttf"));
List<TableRow> buildTable(
{ Context context, int count = 10, bool repeatHeader = false}) {
final rows = <TableRow>[];
{
final tableRow = <Widget>[];
for (final cell in <String>['Hue', 'Color', 'RGBA']) {
tableRow.add(Container(
alignment: Alignment.center,
margin: const EdgeInsets.all(5),
child: Text(cell, style: Theme.of(context).tableHeader)));
}
rows.add(TableRow(children: tableRow, repeat: repeatHeader));
}
for (var y = 0; y < count; y++) {
final h = math.sin(y / count) * 365;
final PdfColor color = PdfColorHsv(h, 1.0, 1.0);
final tableRow = <Widget>[
Container(
margin: const EdgeInsets.all(5),
child: Text('${h.toInt()}°', style: Theme.of(context).tableCell)),
Container(
margin: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: color,
),
height: Theme.of(context).tableCell.fontSize),
Container(
margin: const EdgeInsets.all(5),
child: Text(color.toHex(), style: Theme.of(context).tableCell)),
];
rows.add(TableRow(children: tableRow));
}
return rows;
}
pdf.addPage(MultiPage(
theme: ThemeData.withFont(
base: myFont,
),
pageFormat: PdfPageFormat.a4,
build: (Context context) {
return [
Column(children: [
Container(
// margin: EdgeInsets.fromLTRB(22, 5, 22, 5),
child: Directionality(
textDirection: TextDirection.rtl,
child: Table(
children: buildTable(context: context, count: 30),
border: TableBorder.all(),
tableWidth: TableWidth.max,
),
),
),
SizedBox(height: 20),
])
];
}));
final String dir = (await getApplicationDocumentsDirectory()).path;
//final String VoucherNo =request['VoucherNo'];
final String path = '$dir/1.pdf';
final File file = File(path);
await file.writeAsBytes(pdf.save());
await Printing.sharePdf(bytes: pdf.save(), filename: 'report.pdf');
}
Are you using any package ?
You can try SPDF package
Related
I want to display individual project data from a Json array in the expansion tile card and then display a list of cards based on the number of projects available. I am able to fetch the data but the card doesn't display or even show on the screen; it is blank and there is no error.
Below is the code for displaying the expansion tile card and making a list:
``` import 'dart:convert';
import 'package:expansion_tile_card/expansion_tile_card.dart';
import 'package:flutter/material.dart';
import 'package:mesys/network_utils/api.dart';
import 'package:mesys/models/dummy_model.dart';
class ProjectWidget extends StatefulWidget {
const ProjectWidget({Key? key}) : super(key: key);
#override
_ProjectWidgetState createState() => _ProjectWidgetState();
}
class _ProjectWidgetState extends State<ProjectWidget> {
GlobalKey<ExpansionTileCardState> cardA = GlobalKey();
final List<ProjectModel> _projects = <ProjectModel>[];
Future<List<ProjectModel>> _fetchProjects() async {
var res = await Network().getData('users/project');
var projects = <ProjectModel>[];
if (res.statusCode == 200) {
var body = json.decode(res.body);
var tdata = body['data'];
var projectsJson = tdata;
for (var projectJson in projectsJson) {
projects.add(ProjectModel.fromJson(projectJson));
}
}
return projects;
}
#override
Widget build(BuildContext context) {
_fetchProjects().then((value) {
_projects.addAll(value);
});
return Container(
child: ListView.builder(
itemCount: _projects.length,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0, vertical: 20),
child: ExpansionTileCard(
baseColor: const Color.fromRGBO(0, 161, 39, 1),
expandedColor: Colors.amber,
key: cardA,
leading: CircleAvatar(
foregroundImage:
Image.asset('assets/images/progress.png')
.image),
title: Text(_projects[index].title,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold)),
subtitle: Text(_projects[index].location,
style: const TextStyle(color: Colors.black)),
children: <Widget>[
const Divider(
thickness: 1,
height: 1,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
child: Text(_projects[index].description),
)),
])),
);
}));
}
} ```
Please help me solve why it isn't displaying, thanks.
I'm creating a app, where one of the features is update text fields
based on changes of another text fields.
The fields are about product prices in: Dollar, Euro, Real.
If the price in Dollar are changed by user, the Euro and Real price are too, and so on...
The problem is, if I use the normal TextEditingController all works well, but if I use MoneyMaskedTextController from the flutter_masked_text package, the updating stops.
Could any one test my code and to answer me why the updating stops with the MoneyMaskedTextController?
To test, don't forget to install the flutter_masked_text: ^0.8.0 in your pubspec.yaml.
If I can not use the flutter_masked_text for it, how could I use masks and update text fields?
Thank you.
import 'package:flutter/material.dart';
import 'package:flutter_masked_text/flutter_masked_text.dart';
void main() {
runApp(MaterialApp(
home: ProductType(),
));
}
class ProductType extends StatefulWidget {
_ProductTypeScreen createState() => _ProductTypeScreen();
}
class _ProductTypeScreen extends State<ProductType> {
#override
Widget build(BuildContext context) {
double dollarRate = 3.70;
double euroRate = 4.20;
//Normal controllers
/* final ctrl_real = TextEditingController();
final ctrl_dollar = TextEditingController();
final ctrl_euro = TextEditingController();*/
//Money Mask controllers
final ctrl_real = MoneyMaskedTextController();
final ctrl_dollar = MoneyMaskedTextController();
final ctrl_euro = MoneyMaskedTextController();
void change_real(String text) {
double real = double.parse(text);
ctrl_dollar.text = (real / dollarRate).toString();
ctrl_euro.text = (real / euroRate).toString();
}
void change_dollar(String text) {
double dolar = double.parse(text);
ctrl_real.text = (dolar * dollarRate).toString();
ctrl_euro.text = (dolar * dollarRate / euroRate).toString();
}
void change_euro(String text) {
double euro = double.parse(text);
ctrl_real.text = (euro * euroRate).toString();
ctrl_dollar.text = (euro * euroRate / dollarRate).toString();
}
return Scaffold(
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
width: 160.0,
height: 200.0,
padding: EdgeInsets.all(20.0),
child: TextField(
controller: ctrl_euro,
decoration: InputDecoration(
labelText: "Euro price",
prefixText: "€"),
onChanged: change_euro,
keyboardType: TextInputType.numberWithOptions(
decimal: true),
)),
Container(
width: 160.0,
height: 200.0,
padding: EdgeInsets.all(20.0),
child: TextField(
controller: ctrl_dollar,
decoration: InputDecoration(
labelText: "Dolar price",
prefixText: "US\$"),
onChanged: change_dollar,
keyboardType: TextInputType.numberWithOptions(
decimal: true),
)),
Container(
width: 160.0,
height: 200.0,
padding: EdgeInsets.all(20.0),
child: TextField(
controller: ctrl_real,
decoration: InputDecoration(
labelText: "Real price",
prefixText: "R\$"),
onChanged: change_real,
keyboardType: TextInputType.numberWithOptions(
decimal: true),
)),
]
)
)
);
}
}
If you check the value received on each method , you will see a comma "," in your decimal values.
void change_real(String text) {
print("Text : $text");
}
so every time you try to parse those values , it crashes here:
double real = double.parse(text);
One way to solve your issue just change the decimalSeparator to '.' , like this:
final ctrl_real = MoneyMaskedTextController(decimalSeparator: ".");
final ctrl_dollar = MoneyMaskedTextController(decimalSeparator: ".");
final ctrl_euro = MoneyMaskedTextController(decimalSeparator: ".");
I'm trying to create a range slider on top of a Row of Containers which should create an audio waveform, but I have no idea where to even start...
The main issue is that the range slider sits right on top of the row of containers and it should change their colors on the "selected" section.
Here's what I currently have:
The code to create the image and details.
class BeatLyricsPage extends StatefulWidget {
final Beat beat;
BeatLyricsPage(this.beat);
#override
_BeatLyricsPageState createState() => _BeatLyricsPageState(beat);
}
class _BeatLyricsPageState extends State<BeatLyricsPage> {
final Beat beat;
final _kPicHeight = 190.0;
// used in _buildPageHeading to add the beat key and beat bpm
Widget _buildBeatInfoItem(String text) => DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: MyColor.white, width: 1.0),
borderRadius: BorderRadius.circular(4.0),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 3.0, horizontal: 12.0),
child: Text(text, style: TextStyle(color: MyColor.white, fontSize: 10.0, fontWeight: FontWeight.w600)),
),
);
final _kAudioControlsWidth = 180.0;
final _kAudioControlsHeight = 36.0;
final _kAudioControlsMainButtonSize = 56.0;
Widget _buildAudioControls(BuildContext context) => Positioned(
left: (MediaQuery.of(context).size.width / 2) - (_kAudioControlsWidth / 2),
top: _kPicHeight - (_kAudioControlsHeight / 2),
child: Stack(
overflow: Overflow.visible,
children: [
Container(
width: _kAudioControlsWidth,
height: _kAudioControlsHeight,
decoration: BoxDecoration(color: MyColor.darkGrey, borderRadius: BorderRadius.circular(100.0)),
padding: EdgeInsets.symmetric(horizontal: LayoutSpacing.sm),
child: Row(
children: [
CButtonLike(beatId: beat.id),
Spacer(),
GestureDetector(
behavior: HitTestBehavior.opaque,
child: Icon(BeatPulseIcons.cart),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LicenseOptionsPage(beat))),
),
],
),
),
// ****** MAIN BUTTON (Play/Pause) ******
Positioned(
left: (_kAudioControlsWidth / 2) - (_kAudioControlsMainButtonSize / 2),
top: (_kAudioControlsHeight - _kAudioControlsMainButtonSize) / 2,
child: Container(
height: _kAudioControlsMainButtonSize,
width: _kAudioControlsMainButtonSize,
decoration: BoxDecoration(
gradient: LinearGradient(begin: Alignment.topLeft, colors: [MyColor.primary, Color(0xFFf80d0a)]),
borderRadius: BorderRadius.circular(100.0)),
child: CButtonPlay(),
),
)
],
),
);
Widget _buildWaveForm() {
// creates a random list of doubles, "fake data"
var rng = Random();
final List waveFormData = [];
for (var i = 0; i < 90; i++) {
waveFormData.add(rng.nextInt(45).toDouble());
}
// player bloc
final playerBloc = BlocProvider.getPlayerBloc(context);
// renders
return Container(
height: _kPicHeight,
padding: EdgeInsets.symmetric(vertical: LayoutSpacing.xxxl),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// current playing second
StreamBuilder<double>(
stream: playerBloc.playingSecond,
initialData: 0.0,
builder: (_, playingSecondSnapshot) {
// current beat playing
return StreamBuilder<Beat>(
stream: playerBloc.playingBeat,
builder: (_, playingBeatSnapshot) {
final playingBeat = playingBeatSnapshot.data;
// if the beat playing is the same as the beat selected for the lyrics, show playing seconds
if (playingBeat?.id == beat.id)
return Text(secondsToTime(playingSecondSnapshot.data), style: MyFontStyle.sizeXxs);
// otherwise show 0:00
else
return Text(secondsToTime(0), style: MyFontStyle.sizeXxs);
},
);
},
),
SizedBox(width: LayoutSpacing.xs),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: waveFormData
.map((waveFormDataIndex) => Container(
height: waveFormDataIndex > 5.0 ? waveFormDataIndex : 5.0,
width: 2,
color: MyColor.white,
margin: EdgeInsets.only(right: 1),
))
.toList(),
),
SizedBox(width: LayoutSpacing.xs),
Text(secondsToTime(beat.length), style: MyFontStyle.sizeXxs),
],
),
);
}
Widget _buildPageHeading(BuildContext context, {#required String imageUrl}) => Stack(
children: [
Column(
children: [
Hero(
tag: MyKeys.makePlayerCoverKey(beat.id),
child: Opacity(
opacity: 0.3,
child: Container(
height: _kPicHeight,
decoration: BoxDecoration(
image: DecorationImage(image: CachedNetworkImageProvider(imageUrl), fit: BoxFit.cover),
),
),
),
),
Container(color: MyColor.background, height: LayoutSpacing.xl)
],
),
Padding(
padding: EdgeInsets.all(LayoutSpacing.xs),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_buildBeatInfoItem(beat.key),
SizedBox(width: 4.0),
_buildBeatInfoItem('${beat.bpm} BPM'),
],
),
),
_buildAudioControls(context),
_buildWaveForm(),
],
);
}
To create a custom range slider, you can use the GestureRecognizer and save the position of each slider in variable inside a StatefulWidget. To decide wether a bar with the index i is inside the range, you can divide the pixel position of the limiter(bar1&bar2 in the source below) by the width of bars and compare it to i.
Sadly I couldn't work with your code example. Instead I created a bare minimum example as you can see below. If you take a minute to read into, I'm sure you can transfer it to your application.
import 'dart:math';
import 'package:flutter/material.dart';
List<int> bars = [];
void main() {
// generate random bars
Random r = Random();
for (var i = 0; i < 50; i++) {
bars.add(r.nextInt(200));
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> {
static const barWidth = 5.0;
double bar1Position = 60.0;
double bar2Position = 180.0;
#override
Widget build(BuildContext context) {
int i = 0;
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.centerLeft,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: bars.map((int height) {
Color color =
i >= bar1Position / barWidth && i <= bar2Position / barWidth
? Colors.deepPurple
: Colors.blueGrey;
i++;
return Container(
color: color,
height: height.toDouble(),
width: 5.0,
);
}).toList(),
),
Bar(
position: bar2Position,
callback: (DragUpdateDetails details) {
setState(() {
bar2Position += details.delta.dx;
});
},
),
Bar(
position: bar1Position,
callback: (DragUpdateDetails details) {
setState(() {
bar1Position += details.delta.dx;
});
},
),
],
),
),
);
}
}
class Bar extends StatelessWidget {
final double position;
final GestureDragUpdateCallback callback;
Bar({this.position, this.callback});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(left: position >= 0.0 ? position : 0.0),
child: GestureDetector(
onHorizontalDragUpdate: callback,
child: Container(
color: Colors.red,
height: 200.0,
width: 5.0,
),
),
);
}
}
in order to have a wave slider :
class WaveSlider extends StatefulWidget {
final double initialBarPosition;
final double barWidth;
final int maxBarHight;
final double width;
WaveSlider({
this.initialBarPosition = 0.0,
this.barWidth = 5.0,
this.maxBarHight = 50,
this.width = 60.0,
});
#override
State<StatefulWidget> createState() => WaveSliderState();
}
class WaveSliderState extends State<WaveSlider> {
List<int> bars = [];
double barPosition;
double barWidth;
int maxBarHight;
double width;
int numberOfBars;
void randomNumberGenerator() {
Random r = Random();
for (var i = 0; i < numberOfBars; i++) {
bars.add(r.nextInt(maxBarHight - 10) + 10);
}
}
_onTapDown(TapDownDetails details) {
var x = details.globalPosition.dx;
print("tap down " + x.toString());
setState(() {
barPosition = x;
});
}
#override
void initState() {
super.initState();
barPosition = widget.initialBarPosition;
barWidth = widget.barWidth;
maxBarHight = widget.maxBarHight.toInt();
width = widget.width;
if (bars.isNotEmpty) bars = [];
numberOfBars = width ~/ barWidth;
randomNumberGenerator();
}
#override
Widget build(BuildContext context) {
int barItem = 0;
return Scaffold(
backgroundColor: Colors.grey[900],
body: Center(
child: GestureDetector(
onTapDown: (TapDownDetails details) => _onTapDown(details),
onHorizontalDragUpdate: (DragUpdateDetails details) {
setState(() {
barPosition = details.globalPosition.dx;
});
},
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: bars.map((int height) {
Color color = barItem + 1 < barPosition / barWidth
? Colors.white
: Colors.grey[600];
barItem++;
return Row(
children: <Widget>[
Container(
width: .1,
height: height.toDouble(),
color: Colors.black,
),
Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(1.0),
topRight: const Radius.circular(1.0),
),
),
height: height.toDouble(),
width: 4.8,
),
Container(
width: .1,
height: height.toDouble(),
color: Colors.black,
),
],
);
}).toList(),
),
),
),
),
);
}
}
and use it like :
WaveSlider(
initialBarPosition: 180.0,
barWidth: 5.0,
maxBarHight: 50,
width: MediaQuery.of(context).size.width,
)
Widget build(BuildContext context) {
TextField XnumField = new TextField(
keyboardType: TextInputType.numberWithOptions(),
decoration: new InputDecoration(labelText: "X array"),
onSubmitted: (String text){
for(i = 0; i < 4; i++){
X[i] = int.parse(text);
}
},
);
Hi,
If you are looking for something like this here you go.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new DemoScreen(),
));
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => new _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
List<int> _myList = new List();
TextEditingController _myController = new TextEditingController();
String _result = "";
String _inputList = "";
setSum() {
int sum = 0;
for (int i = 0; i < _myList.length; i++) {
sum += _myList[i];
if (i == 0)
_inputList = "${_myList[i]}";
else
_inputList = _inputList + " + ${_myList[i]}";
}
_result = "$sum";
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Demo App"),
),
body: new Column(
children: <Widget>[
new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
child: new Text(
_inputList,
style: new TextStyle(fontSize: 40.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(vertical: 25.0),
child: new Text(
_result,
style: new TextStyle(fontSize: 70.0),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 50.0),
child: new TextField(
controller: _myController,
keyboardType: TextInputType.number,
onSubmitted: (text) {
setState(() {
_myList.add(int.parse(text));
setSum();
_myController.clear();
});
},
),
)
],
),
);
}
}
Hope it helps :)
Something like ...
import 'dart:core';
List<int> BuildIntArray(String input) {
var outList = new List<int>();
final _delimiter = ',';
final _values = input.split(_delimiter);
_values.forEach((item) {
outList.add(int.parse(item));
});
return outList;
}
Which would ...
import 'package:IntegerArray/IntegerArray.dart' as IntegerArray;
main(List<String> arguments) {
final input = "1,2,3,4,5";
final intInputValues = IntegerArray.BuildIntArray(input);
print (intInputValues);
int sum = 0;
intInputValues.forEach((item) {
sum+=item;
});
print (sum);
}
... do this ...
Observatory listening on http://127.0.0.1:64499/
[1, 2, 3, 4, 5]
15
Process finished with exit code 0
I don't see a "tryParse" to filter out non-numeric values ... but with some validation/error checking you could add to this ...
I'm trying to create a word game. I have a local json file where I'm retrieving data from. I'm able to retrieve the data and display it on the first row. What I'm trying to do is on tap of one block (on the first row), get the value and display it in order on the second row.
I'm able to retrieve the value but I can't display it on the second row. I tested this by printing the value in the console.
Updated code:
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle
.of(context)
.loadString('data_repo/starwars_data.json'),
builder: (context, snapshot) {
var newData = JSON.decode(snapshot.data.toString());
List<Widget> listMyWidgets() {
List<Widget> list = new List();
for (var i = 0; i < newData.length; i++) {
var word = newData[i]['word']["letters"];
for (var n = 0; n < word.length; n++) {
list.add(new Text(word[n]['letter']));
}
}
return list;
}
List letters = [];
for (int i = 0; i < listMyWidgets().length; i++) {
var listMyWidgetsToString =
listMyWidgets()[i].toString();
var listWidgetToList =
listMyWidgetsToString.replaceAll('Text("', "");
var completeWordList =
listWidgetToList.replaceAll('")', "");
letters.add(completeWordList);
}
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
new Column(children: [
new Image.asset(newData[0]['image'])
]),
new GridView.count(
shrinkWrap: true,
crossAxisCount: listMyWidgets().length,
children: new List.generate(
listMyWidgets().length,
(i) => new GestureDetector(
onTap: () {
final int wordLength =
5; //this is a ref to the lenght of the word so you do not keep adding tiles
setState(() {
(letters.length + 1) <=
wordLength * 2
? letters.add(letters[i])
: null;
});
},
child: new Card(
elevation: 5.0,
color: Colors.brown[500],
child: new Padding(
padding:
const EdgeInsets.all(8.0),
child: new Center(
child:
new Text(letters[i])),
),
),
)),
),
],
),
);
},
itemCount: newData == null ? 0 : newData.length,
);
}),
),
)
It depends on how you want to structure your data. In this example, I just add the pressed letters into the same array for the word and it will do the job.
Note that I keep a reference (which you may add to your JSON) which is the initial length of the word so it stops adding tiles when all letters are used.
Also you need to have a StatefulWidget in order for this to work
Probably there is a better a way to handle this but that is what I managed to do atm.
class GridViewWords extends StatefulWidget {
#override
GridViewWordsState createState() {
return new GridViewWordsState();
}
}
class GridViewWordsState extends State<GridViewWords> {
List letters = [
"A",
"M",
"C",
"I",
"C"
];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new GridView.count(
shrinkWrap: true,
crossAxisCount: 5,
children: new List.generate(letters.length, (i)=>
new GestureDetector(
onTap: (){
final int wordLength =5; //this is a ref to the lenght of the word so you do not keep adding tiles
setState((){
(letters.length+1)<=wordLength*2? letters.add(letters[i]):null;
});
},
child: new Card(
elevation: 5.0,
color: Colors.brown[500],
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(child: new Text(letters[i])),
),
),
)),
),
);
}
}