Basically I want to achieve exactly the same thing as Flutter: How to hide or show more text within certain length.
Here is my code snippet.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final TextSpan span = TextSpan(
text: text,
style: TextStyle(
fontSize: 13,
),
);
final TextPainter textPainter = TextPainter(
text: span,
maxLines: 1,
ellipsis: '...',
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: constraints.maxWidth);
if (textPainter.didExceedMaxLines)
return Row(
crossAxisAlignment: _basicInformationIsExpanded
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 13,
),
maxLines: _isExpanded ? null : 1,
//overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
child: _isExpanded
? Icon(
Icons.expand_less,
)
: Icon(
Icons.expand_more,
),
onTap: () {
setState(() => _isExpanded =
!_isExpanded);
},
),
],
);
else
return Text(
text,
style: TextStyle(
fontSize: 13,
),
);
}),
The weird thing is if I comment overflow: TextOverflow.ellipsis,, everything is fine. But I need to show the ellipsis and if I add that line, the text doesn't expand when I click the icon.
Can anyone help me with it? Thanks.
You can copy paste run full code below
You can set overflow based on _isExpanded
overflow: _isExpanded ? null : TextOverflow.ellipsis,
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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 _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>[
Container(
width: 200,
child: ExpandText(
text: "long string" * 10,
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ExpandText extends StatefulWidget {
String text;
ExpandText({this.text});
#override
_ExpandTextState createState() => _ExpandTextState();
}
class _ExpandTextState extends State<ExpandText> {
bool _isExpanded = false;
bool _basicInformationIsExpanded = true;
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final TextSpan span = TextSpan(
text: widget.text,
style: TextStyle(
fontSize: 13,
),
);
final TextPainter textPainter = TextPainter(
text: span,
maxLines: 1,
ellipsis: '...',
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: constraints.maxWidth);
if (textPainter.didExceedMaxLines) {
print("exceed");
return Row(
crossAxisAlignment: _basicInformationIsExpanded
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Text(
widget.text,
style: TextStyle(
fontSize: 13,
),
maxLines: _isExpanded ? null : 1,
overflow: _isExpanded ? null : TextOverflow.ellipsis,
),
),
GestureDetector(
child: _isExpanded
? Icon(
Icons.expand_less,
)
: Icon(
Icons.expand_more,
),
onTap: () {
setState(() => _isExpanded = !_isExpanded);
},
),
],
);
} else {
print("not exceed");
return Text(
widget.text,
style: TextStyle(
fontSize: 13,
),
);
}
});
}
}
A long ago i stumbled onto same thing, surely using these widget's is a way to do this,
but here is the code which i wrote and its totally customizable.
You can change the limit variable to use it accordinly
class QNAContainer extends StatefulWidget {
final String ques;
final String answer;
QNAContainer({#required this.ques, #required this.answer});
#override
_QNAContainerState createState() => _QNAContainerState();
}
class _QNAContainerState extends State<QNAContainer> {
String truncAns;
bool showingAll = false;
int limit = 80;
#override
void initState() {
super.initState();
if (widget.answer.length > limit ) {
print("truncc");
truncAns = widget.answer.toString().substring(0, limit) + '...';
} else {
truncAns = widget.answer;
}
}
#override
Widget build(BuildContext context) {
ScreenUtil.instance = ScreenUtil(
width: Styles.get_width(context),
height: Styles.get_height(context),
allowFontScaling: true);
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: ScreenUtil().setWidth(10), vertical: ScreenUtil().setHeight(10)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: AppColors.greyFillColor.withOpacity(0.6),
),
margin: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(7)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.ques,
style: TextStyle(
fontSize: ScreenUtil().setHeight(14),
fontWeight: FontWeight.bold,
)),
SizedBox(height: ScreenUtil().setHeight(5)),
Text(showingAll ? widget.answer : truncAns,
style: TextStyle(
fontSize: ScreenUtil().setHeight(14),
)),
SizedBox(height: ScreenUtil().setHeight(5)),
truncAns.contains('...')
? GestureDetector(
onTap: () {
setState(() {
showingAll = !showingAll;
});
},
child: Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(5)),
padding: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(5), horizontal: ScreenUtil().setWidth(9)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColors.kDefaultPink),
child: Text(
showingAll ? 'see less' : 'see more',
style: TextStyle(color: Colors.white, fontSize: ScreenUtil().setHeight(14)),
),
),
),
)
: SizedBox()
],
),
);
}
}
Related
I want to make a program where 2 options will be written on text and I want to make my program whenever the user choose one of those option the other option will be greyed out.
I've tried using text button, but it's still a bit wacky for my taste.
Try this:
class _MyHomePageState extends State<MyHomePage> {
int _selectIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
children: [
InkWell(
onTap: () {
setState(() {
_selectIndex = 0;
});
},
child: Text(
'Celcius',
style: TextStyle(
color: _selectIndex == 0 ? Colors.black : Colors.grey,
),
),
),
const Text(' | '),
InkWell(
onTap: () {
setState(() {
_selectIndex = 1;
});
},
child: Text(
'Fashrenheit',
style: TextStyle(
color: _selectIndex == 1 ? Colors.black : Colors.grey,
),
),
),
],
),
),
);
}
}
You can use stateful widget to change states when button is pressed.
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
enum Temperature { celcius, fahrenheit }
class _HomePageState extends State<HomePage> {
late Temperature _currentScale;
late num _currentTemp;
#override
void initState() {
super.initState();
_currentScale = Temperature.celcius;
_currentTemp = 24; //multiply by 1.8 add 32 C->F
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_currentScale == Temperature.celcius
? '$_currentTemp°C'
: '${_currentTemp * 1.8 + 32}°F',
style: Theme.of(context).textTheme.headline2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
child: Text(
'Celcius',
style: TextStyle(
color: _currentScale == Temperature.celcius
? Colors.red
: Colors.grey),
),
onPressed: () =>
setState(() => _currentScale = Temperature.celcius)),
const SizedBox(
width: 10,
),
TextButton(
child: Text('Fahrenheit',
style: TextStyle(
color: _currentScale == Temperature.fahrenheit
? Colors.red
: Colors.grey)),
onPressed: () => setState(
() => _currentScale = Temperature.fahrenheit))
],
)
],
),
),
),
);
}
}
You can try ToggleButton instead of TextButton
ToggleButton
You can try this one. I hope this is useful to you !
class MyHomePage extends StatelessWidget {
final RxInt _selectIndex = 0.obs;
MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(
() => Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"25",
style: TextStyle(
fontSize: 30,
color: _selectIndex.value == 0 ? Colors.grey : Colors.black,
),
),
const SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
InkWell(
onTap: () {
_selectIndex.value = 0;
},
child: Text(
'Celcius',
style: TextStyle(
color: _selectIndex.value == 0 ? Colors.black : Colors.grey,
),
),
),
const Text(' | '),
InkWell(
onTap: () {
_selectIndex.value = 1;
},
child: Text(
'Fashrenheit',
style: TextStyle(
color: _selectIndex.value == 1 ? Colors.black : Colors.grey,
),
),
),
],
),
],
),
),
),
);
}
}
I am building an online bottle store app using flutter and I am having an issue where if I add a product to favorites the selected product's button won't stay selected on the home page if I switch pages. I have categorized the products using a Tabbar and Tabbarview. I have tried using AutomaticKeepAliveClientMxin to keep the page alive but with no success. Please can anyone assist.
Here's what happens:
I click on the selected product
then it is added to Favorites
Come back to the home page and the selected item is no longer showing that it is selected
Here's my code:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
ProductProvider productProvider = ProductProvider();
late TabController tabController;
#override
void initState() {
super.initState();
tabController = TabController(length: 4, vsync: this);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
var cart = Provider.of<ShoppingCartProvider>(context);
var favoriteProvider = Provider.of<FavoriteProvider>(context);
Size _screenSize = MediaQuery.of(context).size;
final double itemHeight = (_screenSize.height - kToolbarHeight - 24) / 2;
final double itemWidth = _screenSize.width / 2;
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Categories',
style: TextStyle(
fontSize: 20.0,
fontFamily: 'Montserrat-ExtraBold',
fontWeight: FontWeight.bold),
),
),
Container(
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
controller: tabController,
indicator:
CircleTabIndicator(color: Colors.redAccent, radius: 4.0),
isScrollable: true,
labelColor: Colors.redAccent,
labelStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0),
unselectedLabelColor: Colors.black,
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0),
tabs: const [
Tab(text: 'Brandy'),
Tab(text: 'Gin'),
Tab(text: 'Soft drinks'),
Tab(text: 'Whiskey')
],
),
),
),
Container(
height: 400,
width: double.maxFinite,
child: TabBarView(
controller: tabController,
children: productProvider.categories.map((bottleCategory) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: itemWidth / itemHeight,
),
itemCount: bottleCategory.bottleList.length,
itemBuilder: (context, index) {
return Card(
shadowColor: Colors.grey,
surfaceTintColor: Colors.amber,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Stack(
children: [
Positioned(
right: 0,
child: InkWell(
onTap: () {
favoriteProvider.toggleFavorites(
bottleCategory.bottleList[index]);
if (favoriteProvider.isExist(
bottleCategory.bottleList[index])) {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Added to Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
),
);
} else {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Removed from Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.red,
duration: Duration(seconds: 1),
),
);
}
},
child: favoriteProvider.isExist(
bottleCategory.bottleList[index])
? const Icon(
Icons.favorite,
color: Colors.redAccent,
)
: const Icon(Icons.favorite_border),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Image.asset(
bottleCategory.bottleList[index].image,
height: 200.0,
),
),
Center(
child: Text(
bottleCategory
.bottleList[index].bottleName,
style: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold))),
Center(
child: Text(
'R${bottleCategory.bottleList[index].price}'),
)
],
),
Positioned(
bottom: 0,
right: 10,
child: IconButton(
icon: const Icon(Icons.add_circle),
iconSize: 40.0,
onPressed: () {
cart.addToCart(
bottleCategory.bottleList[index].id,
bottleCategory
.bottleList[index].bottleName,
bottleCategory
.bottleList[index].price,
bottleCategory
.bottleList[index].image);
},
))
],
),
);
},
);
}).toList()),
),
],
),
),
);
}
}
class FavoriteProvider with ChangeNotifier {
List<Bottle> _favItems = [];
List<Bottle> get favItems {
return [..._favItems];
}
void toggleFavorites(Bottle favBottle) {
final isExist = _favItems.contains(favBottle);
if (isExist) {
_favItems.remove(favBottle);
} else {
_favItems.add(favBottle);
}
notifyListeners();
}
bool isExist(Bottle favBottle) {
final isExist = _favItems.contains(favBottle);
return isExist;
}
void clearFavorite() {
_favItems = [];
notifyListeners();
}
}
Try using Consumer widget. Like so:
Consumer<favoriteProvider>(
builder: (BuildContext context, favorite, _){
return Icon(
Icons.favorite,
color: favorite.isExist(bottleCategory.bottleList[index])? Colors.redAccent : null,
);
},
),
Consumer widget will refresh or change the state whenever the ChangeNotifier of that model, in this case, FavoriteProvider is triggered, this should allows your widget to change and check itself anytime. So you shouldn't need to keep your state or screen alive all the time.
If that doesn't work, please change your Business Logic in the FavoriteProvider. Instead of using contains, I suggest to use any and identifies each instances with its own id or any of its unique variable. Like so:
bool isExist(Bottle favBottle) {
final isExist = _favItems.any((e) =>e.bottleName == favBottle.bottleName);
return isExist;
}
What i have ? :
Now i can enter text into blue section, after click on it keyboard shows and every widget adjust to another (by mainAxisAlignment: MainAxisAlignment.spaceAround).
What i want to have ? :
I want the same thing as i have but i want to be able to click on red section to show up keyboard. (while keeping adjusting widgets)
shortened version of
main.dart :
import 'package:flutter/material.dart';
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
GestureDetector(
child: const TextField(
textAlign: TextAlign.center,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Enter Word",
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width,
child: const CustomPaint(
// foregroundPainter: LinePainter(),
),
),
const Text(
"Nie ma takowego słowa",
textAlign: TextAlign.center,
),
])));
}
To do this, you can wrap your TextField in an Expanded widget, which will expand to fill its parent and then set the expanded parameter of the TextField to true and maxLines to null. This will allow the TextField to expand to match its parent's (the Expanded widget's) height:
Expanded(
child: TextField(
expands: true,
maxLines: null,
textAlign: TextAlign.center,
textAlignVertical: TextAlignVertical.center,
),
)
If you do not want your textfield itself to expand, you need to use a
Expanded(
child: GestureDetector(
child: Center(child: TextField(focusNode: _focusNode))
),
)
and use the focusNode of the TextField to request focus for it when tapping the GestureDetector. Example here: https://docs.flutter.dev/cookbook/forms/focus. This will require using a StatefulWidget, as the focusNode is long-lived state.
final code (not shortened)
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:xmltranslator/painter.dart';
import 'package:xmltranslator/xmlreader.dart';
void main() {
runApp(const MyApp());
WidgetsFlutterBinding.ensureInitialized();
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage("text"),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage(String text, {Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
final _searchValue = TextEditingController();
String _typedText = "finding txt";
String _translatedText1 = "translation";
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
getXmlFile(context, _searchValue.text, context);
super.initState();
myFocusNode = FocusNode();
}
late FocusNode myFocusNode;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 77, 77, 77),
appBar: AppBar(
centerTitle: true,
backgroundColor: const Color.fromARGB(255, 47, 47, 47),
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RichText(
text: TextSpan(children: [
TextSpan(
text: "T r a n ",
style: GoogleFonts.overpass(
fontWeight: FontWeight.bold, fontSize: 30)),
TextSpan(
text: " S l a t e",
style: GoogleFonts.overpass(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30)),
]),
),
],
),
elevation: 20,
),
body: SafeArea(
child: Column(children: [
Expanded(
child: TextField(
textAlignVertical: TextAlignVertical.center,
style: GoogleFonts.overpass(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 23),
controller: _searchValue,
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: "Enter Word",
),
onEditingComplete: () async {
String xmlString = await DefaultAssetBundle.of(context)
.loadString("assets/test.xml");
final _translatedText2 =
await getXmlFile(context, _searchValue.text, xmlString);
setState(() {
_typedText = _searchValue.text;
});
if (xmlString.contains(_typedText)) {
setState(() {
_translatedText1 = _translatedText2.first.toString();
});
} else {
setState(() {
_translatedText1 = "Nie ma takowego słowa";
});
print("wartosc po funkcji:$_translatedText2");
}
},
),
),
SizedBox(
width: MediaQuery.of(context).size.width,
child: CustomPaint(
foregroundPainter: LinePainter(),
),
),
Expanded(
child: Text(_translatedText1,
textAlign: TextAlign.center,
style: GoogleFonts.overpass(
color: Colors.red,
fontSize: 30,
)),
),
])));
}
}
i want to edit a listview item when i click on it. I managed (with inkwell) that when I click on a listview item, the bottomsheet opens again where I also create new listview items, but I just can't edit it. I've tried everything I know (I don't know much I'm a beginner). here my codes.
--main.dart--
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '/model/transaction.dart';
import '/widget/chart.dart';
import '/widget/new_transaction.dart';
import '/widget/transactoin_list.dart';
void main() {
// WidgetsFlutterBinding.ensureInitialized();
// SystemChrome.setPreferredOrientations(
// [
// DeviceOrientation.portraitUp,
// DeviceOrientation.portraitDown,
// ],
// );
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale("de"),
Locale("en"),
],
debugShowCheckedModeBanner: false,
title: "URLI",
theme: ThemeData(
primarySwatch: Colors.lightGreen,
fontFamily: "JosefinSans",
textTheme: ThemeData()
.textTheme
.copyWith(
headline4: const TextStyle(
fontFamily: "Tochter",
fontSize: 21,
),
headline5: const TextStyle(
fontFamily: "Bombing",
fontSize: 27,
letterSpacing: 3,
),
headline6: const TextStyle(
fontSize: 21,
fontWeight: FontWeight.w900,
),
)
.apply(
bodyColor: Colors.orangeAccent,
displayColor: Colors.orangeAccent.withOpacity(0.5),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
onPrimary: Colors.white,
primary: Theme.of(context).appBarTheme.backgroundColor,
textStyle: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
appBarTheme: const AppBarTheme(
titleTextStyle: TextStyle(
fontSize: 60,
fontFamily: "Tochter",
),
),
),
home: const AusgabenRechner(),
);
}
}
class AusgabenRechner extends StatefulWidget {
const AusgabenRechner({Key? key}) : super(key: key);
#override
State<AusgabenRechner> createState() => _AusgabenRechnerState();
}
class _AusgabenRechnerState extends State<AusgabenRechner> {
void _submitAddNewTransaction(BuildContext ctx) {
showModalBottomSheet(
context: ctx,
builder: (_) {
return GestureDetector(
onTap: () {},
child: NewTransaction(addNewTx: _addNewTransaction),
behavior: HitTestBehavior.opaque,
);
},
);
}
bool _showChart = false;
final List<Transaction> _userTransactions = [
// Transaction(
// id: "tx1",
// tittel: "Schuhe",
// preis: 99.99,
// datum: DateTime.now(),
// ),
// Transaction(
// id: "tx2",
// tittel: "Jacke",
// preis: 39.99,
// datum: DateTime.now(),
// ),
];
List<Transaction> get _recentTransactions {
return _userTransactions
.where(
(tx) => tx.datum.isAfter(
DateTime.now().subtract(
const Duration(days: 7),
),
),
)
.toList();
}
void _addNewTransaction(
String txTittel,
double txPreis,
DateTime choosenDate,
) {
final newTx = Transaction(
id: DateTime.now().toString(),
tittel: txTittel,
preis: txPreis,
datum: choosenDate,
);
setState(() {
_userTransactions.add(newTx);
});
}
void _deletedTransaction(String id) {
setState(() {
_userTransactions.removeWhere((tdddx) => tdddx.id == id);
});
}
#override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final isInLandscape = mediaQuery.orientation == Orientation.landscape;
final appBar = AppBar(
centerTitle: true,
toolbarHeight: 99,
actions: [
IconButton(
onPressed: () => _submitAddNewTransaction(context),
icon: const Icon(
Icons.add,
color: Colors.white,
),
),
],
title: const Text(
"Ausgaben",
),
);
final txListWidget = SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.45,
child: TransactionList(
transaction: _userTransactions,
delettx: _deletedTransaction,
showNewTransaction: _submitAddNewTransaction,
),
);
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
children: [
if (isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Chart anzeigen",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(width: 9),
Switch.adaptive(
inactiveTrackColor:
Theme.of(context).primaryColor.withOpacity(0.3),
activeColor: Theme.of(context).primaryColor,
value: _showChart,
onChanged: (val) {
setState(() {
_showChart = val;
});
},
),
],
),
),
if (!isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.24,
child: Chart(
recentTransactions: _recentTransactions,
),
),
if (!isInLandscape)
SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.65,
child: txListWidget),
if (isInLandscape)
_showChart
? SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.51,
child: Chart(
recentTransactions: _recentTransactions,
),
)
: SizedBox(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top) *
0.81,
child: txListWidget)
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(
Icons.add,
color: Colors.white,
),
onPressed: () => _submitAddNewTransaction(context),
),
);
}
}
--transaction_list.dart--
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '/model/transaction.dart';
class TransactionList extends StatefulWidget {
const TransactionList({
Key? key,
required this.transaction,
required this.delettx,
required this.showNewTransaction,
}) : super(key: key);
final List<Transaction> transaction;
final Function delettx;
final Function showNewTransaction;
#override
State<TransactionList> createState() => _TransactionListState();
}
class _TransactionListState extends State<TransactionList> {
#override
Widget build(BuildContext context) {
return widget.transaction.isEmpty
? LayoutBuilder(
builder: (ctx, contrains) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Keine Daten vorhanden!",
style: Theme.of(context).textTheme.headline6,
),
const SizedBox(
height: 30,
),
SizedBox(
height: contrains.maxHeight * 0.45,
child: Image.asset(
"assets/images/schlafen.png",
fit: BoxFit.cover,
),
)
],
);
},
)
: Align(
alignment: Alignment.topCenter,
child: ListView.builder(
shrinkWrap: true,
reverse: true,
itemCount: widget.transaction.length,
itemBuilder: (ctx, index) {
return InkWell(
onLongPress: () => widget.showNewTransaction(ctx),
child: Card(
elevation: 5,
child: ListTile(
leading: CircleAvatar(
radius: 33,
child: Padding(
padding: const EdgeInsets.all(9.0),
child: FittedBox(
child: Row(
children: [
Text(
widget.transaction[index].preis
.toStringAsFixed(2),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
),
),
const Text(
"€",
style: TextStyle(
fontSize: 21,
),
)
],
),
),
),
),
title: Text(
widget.transaction[index].tittel,
style: Theme.of(context).textTheme.headline6,
),
subtitle: Text(
DateFormat.yMMMMd("de")
.format(widget.transaction[index].datum),
style: Theme.of(context).textTheme.headline4,
),
trailing: MediaQuery.of(context).size.width > 460
? TextButton.icon(
onPressed: () =>
widget.delettx(widget.transaction[index].id),
icon: const Icon(
Icons.delete_outline,
),
label: const Text("Löschen"),
style: TextButton.styleFrom(
primary: Colors.red,
),
)
: IconButton(
onPressed: () =>
widget.delettx(widget.transaction[index].id),
icon: const Icon(
Icons.delete_outline,
color: Colors.red,
),
),
),
),
);
},
),
);
}
}
--new_transaction.dart--
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
const NewTransaction({Key? key, required this.addNewTx}) : super(key: key);
final Function addNewTx;
#override
State<NewTransaction> createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _tittelcontroller = TextEditingController();
final _preiscontroller = TextEditingController();
DateTime? _selectedDate;
void _submitData() {
final enteredTittel = _tittelcontroller.text;
final enteredPreis = double.parse(_preiscontroller.text);
if (_preiscontroller.text.isEmpty) {
return;
}
if (enteredTittel.isEmpty || enteredPreis <= 0 || _selectedDate == null) {
return;
}
widget.addNewTx(
_tittelcontroller.text,
double.parse(_preiscontroller.text),
_selectedDate,
);
Navigator.of(context).pop();
}
void _presentDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2022),
lastDate: DateTime.now(),
).then((pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
bottom: false,
child: SingleChildScrollView(
child: Container(
//height: MediaQuery.of(context).size.height * 0.5,
padding: EdgeInsets.only(
top: 10,
left: 18,
right: 18,
bottom: MediaQuery.of(context).viewInsets.bottom + 10,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextButton(
onPressed: _submitData,
child: Text(
"hinzufügen",
style: Theme.of(context).textTheme.headlineSmall,
),
),
TextField(
controller: _tittelcontroller,
onSubmitted: (_) => _submitData(),
decoration: const InputDecoration(
label: Text("Tittel"),
),
),
TextField(
controller: _preiscontroller,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
onSubmitted: (_) => _submitData(),
decoration: const InputDecoration(
label: Text("Preis"),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 66),
child: Center(
child: Column(
children: [
Text(
_selectedDate == null
? "Kein Datum ausgewählt"
: DateFormat.yMMMMEEEEd("de")
.format(_selectedDate!),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(
height: 21,
),
ElevatedButton(
style: Theme.of(context).elevatedButtonTheme.style,
onPressed: _presentDatePicker,
child: const Text("Datum wählen"),
),
],
),
),
)
],
),
),
),
);
}
}
EN: Here is an excample code, how i would solve this, when i understood the problem. The passing of the variables to the new Class is a bit differnent then in your Code but it works the same.
DE: So hier ist jetzt ein Beispielcode, wie ich es lösen würde, wenn ich das Problem richtig verstanden habe, dabei werden die Variabeln etwas anders als bei dir in die neue Klasse übergeben, funktioniert aber gleich
class testListview extends StatefulWidget {
var transaction;
var delettx;
var showNewTransaction;
//Passing the data to new Class
testListview(this.transaction, this.delettx,
this.showNewTransaction);
#override
State<testListview> createState() => _testListviewState();
}
class _testListviewState extends State<testListview> {
var transaction;
var delettx;
var showNewTransaction;
//Pass the data into the State of the new Class
_testListviewState(this.transaction, this.delettx,
this.showNewTransaction);
var transaction_2;
//The init state will be called in the first initialization of
//the Class
#override
void initState() {
//Pass your transactions to a new variable
setState(() {
transaction_2 = transaction;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: (BuildContext context,
index){
return TextButton(onPressed: (){
//Change the data with onPressed
setState(() {
transaction_2["preis"] = "500";
});
}, child: Text(transaction_2["preis"]));
});}}
I have 4 walkthrough screens, on reaching the ending of the screens when i go to the homepage of my app which is named as TestScreen here,when i press the back button in my phone i again go back to the walkthrough pages which i dont want and it throws an exception too ("Failed assertion: line 1554 pos 12: '!_debugLocked': is not true."). So i was thinking if i make the activity stack null after coming to TestScreen it might work but i am not able to do so. Please help me.
Main.dart
library flutter_walkthrough;
import 'package:flutter/material.dart';
import 'package:comp_apps/walkthrough.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final List<Walkthrough> list = [
Walkthrough(
title: "Title 1",
content: "Content 1",
imageIcon: Icons.restaurant_menu,
),
Walkthrough(
title: "Title 2",
content: "Content 2",
imageIcon: Icons.search,
),
Walkthrough(
title: "Title 3",
content: "Content 3",
imageIcon: Icons.shopping_cart,
),
Walkthrough(
title: "Title 4",
content: "Content 4",
imageIcon: Icons.verified_user,
),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: IntroScreen(list, MaterialPageRoute(builder: (context)=>
TestScreen())).,
);
}
}
class TestScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
automaticallyImplyLeading: false,
),
);
}
}
class IntroScreen extends StatefulWidget {
final List<Walkthrough> walkthroughList;
final MaterialPageRoute pageRoute;
IntroScreen(this.walkthroughList, this.pageRoute);
void skipPage(BuildContext context) {
Navigator.push(context, pageRoute);
}
#override
_IntroScreenState createState() => _IntroScreenState();
}
class _IntroScreenState extends State<IntroScreen> {
final PageController controller = new PageController();
int currentPage = 0;
bool lastPage = false;
void _onPageChanged(int page) {
setState(() {
currentPage = page;
if (currentPage == widget.walkthroughList.length - 1) {
lastPage = true;
} else {
lastPage = false;
}
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Color(0xFFEEEEEE),
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Container(),
flex: 1,
),
Expanded(
flex: 3,
child: PageView(
children: widget.walkthroughList,
controller: controller,
onPageChanged: _onPageChanged,
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FlatButton(
child: Text(
lastPage ? "" : "SKIP",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16.0),
),
onPressed: () => lastPage ? null : widget.skipPage(context),
),
FlatButton(
child: Text(
lastPage ? "GOT IT" : "NEXT",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
onPressed: () => lastPage
? widget.skipPage(context)
: controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn),
)
],
),
)
],
),
);
}
}
Walkthrough.dart
import 'package:flutter/material.dart';
class Walkthrough extends StatefulWidget {
final title;
final content;
final imageIcon;
final imagecolor;
Walkthrough({this.title, this.content, this.imagecolor, this.imageIcon});
#override
_WalkthroughState createState() => _WalkthroughState();
}
class _WalkthroughState extends State<Walkthrough>
with SingleTickerProviderStateMixin {
Animation animation;
AnimationController animationController;
#override
void initState() {
// TODO: implement initState
super.initState();
animationController = AnimationController(vsync: this,duration:
Duration(milliseconds: 500));
animation = Tween(
begin: -250.0, end: 0.0).animate(CurvedAnimation(parent:
animationController, curve: Curves.easeInOut));
animation.addListener(() => setState(() {}));
animationController.forward();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
animationController.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20.0),
child: Material(
animationDuration: Duration(milliseconds: 500),
elevation: 2.0,
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Transform(
transform: Matrix4.translationValues(animation.value, 0.0, 0.0),
child: Text(widget.title,style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.black
),),
),
Transform(
transform: Matrix4.translationValues(animation.value, 0.0, 0.0),
child: Text(widget.content,
softWrap: true,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 15.0,
color: Colors.black,
),),
),
Icon(
widget.imageIcon,
size: 100.0,
color: widget.imagecolor,
)
],
),
),
);
}
}