Related
I have problem, when I run the code it works but when I out from the page and go back it show x2 for the rows its looping without any loop I make in my database 7 rows when I out and return to the same page it shows 14 rows but I have onlu 7 rows.
This is my code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class my_ads extends StatefulWidget {
const my_ads({Key? key}) : super(key: key);
#override
State<my_ads> createState() => _my_adsState();
}
List list = [];
int select_item = 0;
class _my_adsState extends State<my_ads> {
#override
Future MyAds() async {
final response =
await http.get(Uri.parse('https://***.***.***.**/getData.php'));
if (response.statusCode == 200) {
var red = jsonDecode(response.body);
setState(() {
list.addAll(red);
});
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load Ads');
}
}
#override
void initState() {
super.initState();
GetData();
}
GetData() async {
await MyAds();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
margin: const EdgeInsets.only(left: 70, right: 60, top: 50),
height: 54.0,
width: 224.0,
child: Container(
decoration: BoxDecoration(
border:
Border.all(color: const Color(0xffF4AC47), width: 5),
color: const Color(0xff42A9D2),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40))),
child: const Center(
child: Text(
"MyAds",
style: TextStyle(
fontSize: 25,
color: Color(0xff072A52),
fontFamily: 'Cairo'),
textAlign: TextAlign.center,
),
//end logo
)),
),
const SizedBox(
height: 35,
),
Expanded(
child: ListView.builder(
itemCount: list.length,
itemBuilder: ((cts, i) {
return Column(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
height: 180.0,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: const Color(0xff42A9D2), width: 5),
borderRadius: BorderRadius.circular(8)),
child: Row(
children: [
const Expanded(
flex: 3,
child: Image(
image: AssetImage("assets/book.jpg"),
),
),
Expanded(
flex: 6,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${list[i]["book_name"]}",
style: TextStyle(
fontSize: 20, color: Colors.black87),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${list[i]["collage"]}"),
const Icon(Icons.perm_identity_rounded)
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("${list[i]["loc"]}"),
const Icon(Icons.store)
],
),
],
),
)
],
),
),
),
const SizedBox(height: 16),
],
);
}),
),
),
],
),
);
}
}
I tried to add this method to my PHP code $mysqli_connect-> close();
But its not working
This is my PHP code:
<?php
include("config.php");
$sql = "SELECT book_name,loc,com,spe,collage FROM ads";
$res = $connect->query($sql);
while($row = $res -> fetch_assoc()) {
$data[]=$row;
}
echo json_encode($data);
$mysqli_connect-> close();
?>
You declared list outside. It will maintain its state even when you change screens.
Either you declared it inside the state widget or replace :
list.addAll(red);
by :
list = red;
How to create this kind of UI in flutter. with expanded pageview??
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:movie_app/components/bottom_tabs.dart';
import 'package:movie_app/components/card_image.dart';
import 'package:movie_app/components/expandable_text.dart';
import 'package:movie_app/components/heading.dart';
import 'package:movie_app/constants.dart';
import 'package:movie_app/screens/card_details/components/card_details_cast.dart';
import 'package:movie_app/screens/card_details/components/card_details_photos.dart';
import 'package:movie_app/screens/card_details/components/card_details_reviews.dart';
import 'package:movie_app/screens/card_details/components/card_details_tabs.dart';
import '../../../components/my_text.dart';
import '../card_details.dart';
class Body extends StatefulWidget {
const Body({Key key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
PageController _tabsPageController;
int _selectedTab = 0;
#override
void initState() {
_tabsPageController = PageController();
super.initState();
}
#override
void dispose() {
_tabsPageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Stack(
clipBehavior: Clip.none,
children: [
Image.network(
"https://static.wikia.nocookie.net/film-vault/images/c/c8/Furypost.jpg/revision/latest?cb=20171202094520",
height: 260,
fit: BoxFit.cover,
),
Positioned(
top: 100,
left: 180,
child: SvgPicture.asset(
"assets/icons/play icon.svg",
color: kwhitecolor,
height: 50,
),
),
Container(
margin: EdgeInsets.only(top: 200, left: 15, right: 15, bottom: 20),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const CardImage(
image:
"https://m.media-amazon.com/images/M/MV5BMjA4MDU0NTUyN15BMl5BanBnXkFtZTgwMzQxMzY4MjE#._V1_.jpg",
width: 120,
),
const SizedBox(width: 15),
// Text("data"),
Column(
// mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: const [
MyText(
"FURY",
fontSize: 22,
isBold: true,
color: Colors.black,
),
SizedBox(width: 2),
MyText(
"(2014)",
fontSize: 14,
isBold: true,
color: Colors.black,
),
],
),
const MyText(
"Action/Drama/War",
fontWeight: FontWeight.w600,
),
const SizedBox(height: 40),
Row(
children: [
const Icon(Icons.remove_red_eye_outlined,
color: klightgreyColor),
const SizedBox(width: 2),
const MyText("1.5M", isBold: true),
const SizedBox(width: 10),
SvgPicture.asset(
"assets/icons/comment.svg",
height: 21,
color: klightgreyColor,
),
const SizedBox(width: 4),
const MyText("1K", isBold: true),
const SizedBox(width: 10),
SvgPicture.asset(
"assets/icons/clock.svg",
height: 18,
color: klightgreyColor,
),
const SizedBox(width: 4),
const MyText("2h 30min", isBold: true),
],
),
],
),
],
),
const SizedBox(height: 30),
const Heading(
"STORYLINE",
fontSize: 18,
padding: 0,
lineColor: Color.fromARGB(255, 255, 68, 68),
// fontWeight: FontWeight.w800,
),
const SizedBox(height: 10),
const ExpandableText(
text:
"1945, the Allies are making their final push in the European theater. A battle-hardened Army sergeant nallier (Brad Pitt), leading a Sherman tank and a five-man crew, undertakes a deadly mission behind enemy lines. Hopelessly outnumbered, outgunned and saddled with an inexperienced soldier (Logan Lerman) in their midst, Wardaddy and his men face overwhelming odds as they move to strike at the heart of Nazi Germany.",
max: 4,
),
SizedBox(height: 10),
CardDetailsBottomTabs(
selectedTab: _selectedTab,
tabPressed: (num) {
_tabsPageController.animateToPage(num,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic);
},
),
// HERE I WANT TO USE EXPANDED WIDGET INSTEAD OF SIZEDBOX, BUT IT GIVES ME ERROR
SizedBox(
height: 300,
child: PageView(
controller: _tabsPageController,
onPageChanged: (num) {
setState(() {
_selectedTab = num;
});
},
children: const [
CardDetailsCast(),
CardDetailsPhoto(),
CardDetailsReview(),
],
),
),
],
),
),
],
),
);
}
}
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
this is all what I did so far, when I wrap pageview inside expanded widget it gives me error,
So is there any one who could find a way to wrap the pageview with expanded widget???
You can follow CustomScrollView with SliverAppBar. Example from SliverAppBar
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _pinned = true;
bool _snap = false;
bool _floating = false;
// [SliverAppBar]s are typically used in [CustomScrollView.slivers], which in
// turn can be placed in a [Scaffold.body].
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: _pinned,
snap: _snap,
floating: _floating,
expandedHeight: 160.0,
flexibleSpace: const FlexibleSpaceBar(
title: Text('SliverAppBar'),
background: FlutterLogo(),
),
),
const SliverToBoxAdapter(
child: SizedBox(
height: 20,
child: Center(
child: Text('Scroll to see the SliverAppBar in effect.'),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: index.isOdd ? Colors.white : Colors.black12,
height: 100.0,
child: Center(
child: Text('$index', textScaleFactor: 5),
),
);
},
childCount: 20,
),
),
],
),
bottomNavigationBar: BottomAppBar(
child: Padding(
padding: const EdgeInsets.all(8),
child: OverflowBar(
overflowAlignment: OverflowBarAlignment.center,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('pinned'),
Switch(
onChanged: (bool val) {
setState(() {
_pinned = val;
});
},
value: _pinned,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('snap'),
Switch(
onChanged: (bool val) {
setState(() {
_snap = val;
// Snapping only applies when the app bar is floating.
_floating = _floating || _snap;
});
},
value: _snap,
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('floating'),
Switch(
onChanged: (bool val) {
setState(() {
_floating = val;
_snap = _snap && _floating;
});
},
value: _floating,
),
],
),
],
),
),
),
);
}
}
So, I'm pretty new on Flutter (just about a week) and dart but I hope this Question is not toooooo stupid:
I'm trying to create an App for an existing Website (a forum). I dont want to use the flutter WebView, because I want to create an comepletly new Interface (The Website is not optimized for mobile View, that's why I'm builing this App).
The (german) Forum:
https://www.porsche-914.com/forum
I want to fetch the single forum topics as well as the posts itself including username, title and date and then map the text to a custom widget to display it. The Widget Part is no problem, it's already done but I cant figure out how to fetch that data from the website and how to store it.
I don't know, hope you can help me...
Thanks a lot for the taken time.
How the mapping should work:
fetched[index].title, fetched[index].text
Center(
child: NeumorphismPost(
title: fetched[index].title,
titleColor: Theme.of(context).cardColor,
textColor: Theme.of(context).cardColor,
text: fetched[index].text,
width: 370,
onTap: () {
_popupPage(context,
title: fetched[index].title,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
fetched[index].text,
style: TextStyle(
color: Theme.of(context).cardColor,
fontSize: 18),
),
),
);
},
),
),
This is my custom Widget:
import 'package:flutter/material.dart';
class NeumorphismPost extends StatefulWidget {
final double width;
final double height;
final double borderRadius;
final String title;
final String text;
final Color titleColor;
final Color textColor;
final double titleSize;
final double textSize;
final Function onTap;
NeumorphismPost({
this.width = 185,
this.height = 185,
this.title = "Title",
this.text = "",
this.borderRadius = 20,
this.titleColor = const Color(0xFF424242),
this.textColor = const Color(0xFF424242),
this.titleSize = 22,
this.textSize = 18,
required this.onTap,
});
#override
State<NeumorphismPost> createState() => _NeumorphismPostState();
}
class _NeumorphismPostState extends State<NeumorphismPost> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
direction: Axis.vertical,
children: [
SingleChildScrollView(
child: SizedBox(
height: widget.height,
width: widget.width,
child: GestureDetector(
onTap: () {},
child: Scaffold(
backgroundColor: Colors.transparent,
body: SizedBox(
height: widget.height,
width: widget.width,
child: Center(
child: Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius:
BorderRadius.circular(widget.borderRadius),
boxShadow: [
BoxShadow(
color: Theme.of(context).hintColor,
offset: const Offset(5, 5),
blurRadius: 15,
spreadRadius: 1,
),
BoxShadow(
color: Theme.of(context).backgroundColor,
offset: Offset(-5, -5),
blurRadius: 15,
spreadRadius: 1,
)
],
),
child: Wrap(
direction: Axis.horizontal,
children: [
SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: <Widget>[
Wrap(
direction: Axis.horizontal,
children: [
SizedBox(
height: widget.height / 3,
child: Wrap(
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 15,
vertical: 15),
child: Text(
widget.title,
textAlign:
TextAlign.center,
overflow:
TextOverflow.fade,
style: TextStyle(
fontSize:
widget.titleSize,
fontWeight:
FontWeight.bold,
color:
widget.titleColor,
),
),
),
],
),
],
),
),
],
),
Wrap(
direction: Axis.horizontal,
children: [
GestureDetector(
onTap: () {
widget.onTap();
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 5),
child: SizedBox(
height: widget.height / 1.38,
child: Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 15,
vertical: 15),
child: Text(
widget.text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: widget.textSize,
fontWeight:
FontWeight.normal,
color: widget.textColor,
),
),
),
),
),
),
],
),
],
),
),
),
],
),
),
),
),
),
),
),
),
],
),
);
}
}
From my point of view, you want to create a scraper. I checked the target website (https://www.porsche-914.com/forum), it can be scraped without any special technique (they don't have many Ajax calls (you may test by using Postman)). So it is a possible task. My suggestion flow is:
Load the raw HTML using any technique (http, dio, inappwebview ...)
Parse it using BeautifulSoup (https://pub.dev/packages/beautiful_soup_dart) (or any parser; it is just my favorite parser.)
Map it to your existing model.
Here is some example code. Hope it helps:
import 'package:http/http.dart' as http;
import 'package:beautiful_soup_dart/beautiful_soup.dart';
class TestParse {
excuteSample() async {
var url = Uri.parse('https://www.porsche-914.com/forum');
var response = await http.get(url);
BeautifulSoup bs = BeautifulSoup(response.body);
final allHeaderName = bs.findAll('td', attrs: {'class': 'oben'});
allHeaderName.forEach((element) {
print('the header: ${element.text}');
});
}
}
Here is the result:
The final result you need is a long story and long code. Hope this will give you a starting point.
UPDATE: I added the full demo code, using your requested code:
import 'package:beautiful_soup_dart/beautiful_soup.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(const MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final testparse = TestParse();
List<String> yourModelPleaseReplaceThis = [];
#override
void initState() {
super.initState();
excuteRequestAndParse();
}
excuteRequestAndParse() async {
final result =
await testparse.excuteSample(); //[1] i guess you missing this await
setState(() {
yourModelPleaseReplaceThis = result;
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return Center(
child: NeumorphismPost(
title: yourModelPleaseReplaceThis[index],
titleColor: Theme.of(context).cardColor,
textColor: Theme.of(context).cardColor,
text: yourModelPleaseReplaceThis[index],
width: 370,
onTap: () {
//THIS CODE BELOW IS YOUR CODE....
},
),
);
},
itemCount: yourModelPleaseReplaceThis.length,
);
}
}
// BELOW IS TESTING CODE....
class TestParse {
Future<List<String>> excuteSample() async {
var url = Uri.parse('https://www.porsche-914.com/forum');
var response = await http.get(url);
BeautifulSoup bs = BeautifulSoup(response.body);
final allHeaderName = bs.findAll('td', attrs: {'class': 'oben'});
allHeaderName.forEach((element) {
print('the header: ${element.text}');
});
return Future.value(allHeaderName.map((e) => e.text).toList());
}
}
class NeumorphismPost extends StatefulWidget {
final double width;
final double height;
final double borderRadius;
final String title;
final String text;
final Color titleColor;
final Color textColor;
final double titleSize;
final double textSize;
final Function onTap;
NeumorphismPost({
this.width = 185,
this.height = 185,
this.title = "Title",
this.text = "",
this.borderRadius = 20,
this.titleColor = const Color(0xFF424242),
this.textColor = const Color(0xFF424242),
this.titleSize = 22,
this.textSize = 18,
required this.onTap,
});
#override
State<NeumorphismPost> createState() => _NeumorphismPostState();
}
class _NeumorphismPostState extends State<NeumorphismPost> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
direction: Axis.vertical,
children: [
SingleChildScrollView(
child: SizedBox(
height: widget.height,
width: widget.width,
child: GestureDetector(
onTap: () {},
child: Scaffold(
backgroundColor: Colors.transparent,
body: SizedBox(
height: widget.height,
width: widget.width,
child: Center(
child: Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius:
BorderRadius.circular(widget.borderRadius),
boxShadow: [
BoxShadow(
color: Theme.of(context).hintColor,
offset: const Offset(5, 5),
blurRadius: 15,
spreadRadius: 1,
),
BoxShadow(
color: Theme.of(context).backgroundColor,
offset: Offset(-5, -5),
blurRadius: 15,
spreadRadius: 1,
)
],
),
child: Wrap(
direction: Axis.horizontal,
children: [
SingleChildScrollView(
child: Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: <Widget>[
Wrap(
direction: Axis.horizontal,
children: [
SizedBox(
height: widget.height / 3,
child: Wrap(
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 15,
vertical: 15),
child: Text(
widget.title,
textAlign:
TextAlign.center,
overflow:
TextOverflow.fade,
style: TextStyle(
fontSize:
widget.titleSize,
fontWeight:
FontWeight.bold,
color:
widget.titleColor,
),
),
),
],
),
],
),
),
],
),
Wrap(
direction: Axis.horizontal,
children: [
GestureDetector(
onTap: () {
widget.onTap();
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 5),
child: SizedBox(
height: widget.height / 1.38,
child: Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 15,
vertical: 15),
child: Text(
widget.text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: widget.textSize,
fontWeight:
FontWeight.normal,
color: widget.textColor,
),
),
),
),
),
),
],
),
],
),
),
),
],
),
),
),
),
),
),
),
),
],
),
);
}
}
This is the result:
Please note:
This list I created is for sample, so it is just a list of Strings. You should create a complete Model.
I've created an expansiontile and I've changed the original trailing icon to an svg icon which I customized. Anytime I click the expansion tile... everything works but the new svg trailing icon doesnt spin like the original trailing icon. Please how do I fix this?
code below
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class ExpansionTileCard extends StatelessWidget {
final String toptitle;
final List<Widget> children;
const ExpansionTileCard({Key key, this.toptitle, this.children})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
border:
new Border(bottom: new BorderSide(color: Colors.green))),
child: new Row(
children: [
new Expanded(
flex: 9,
child: new Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpansionTile(
title: Text(
toptitle,
style: TextStyle(
color: Colors.black,
letterSpacing: -1,
fontSize: 19,
fontWeight: FontWeight.bold),
),
children: children,
childrenPadding: EdgeInsets.fromLTRB(0, 0, 0, 20),
iconColor: Colors.green,
trailing: SvgPicture.asset(
'assets/icons/textmessage.svg',
height: 30,
),
),
],
),
),
)
],
),
),
],
),
);
}
}
While we don't provide trailing on ExpansionTile it uses the default behavior of it.
We are passing a widget into trailing and we need to control it. In order to do that, we need to check if ExpansionTile is expanded or not, the onExpansionChanged call back will provide this value. To handle animation we can use many widgets like Transform, RotatedBox, AnimatedRotation... In this ExpansionTileCard widget will be extended from StatefulWidget .
Steps
convert to StatefulWidget
create a bool inside state class to check the Expanded-state.
bool _isExpanded = false;
change value on onExpansionChanged
onExpansionChanged: (value) {
setState(() {
_isExpanded = value;
});
},
assign your widget on trailing by wrapping animation widget or just check the state return two widget
_isExpanded? ExpandedWidget:NormalViewWidget()
trailing: AnimatedRotation( /// you can use different widget for animation
turns: _isExpanded ? .5 : 0,
duration: Duration(seconds: 1),
child: CustomWidget()// your svgImage here
),
Widget State
class _ExpansionTileCardState extends State<ExpansionTileCard> {
bool _isExpanded = false;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.green))),
child: Row(
children: [
Expanded(
flex: 9,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpansionTile(
onExpansionChanged: (value) {
setState(() {
_isExpanded = value;
});
},
title: Text(
widget.toptitle,
style: TextStyle(
color: Colors.black,
letterSpacing: -1,
fontSize: 19,
fontWeight: FontWeight.bold),
),
children: widget.children,
childrenPadding: EdgeInsets.fromLTRB(0, 0, 0, 20),
iconColor: Colors.green,
trailing: AnimatedRotation(
turns: _isExpanded ? .5 : 0,
duration: Duration(seconds: 1),
child: SvgPicture.asset(
'assets/icons/textmessage.svg',
height: 30,
),
),
],
),
),
)
],
),
),
],
),
);
}
}
This is the output that I want. I am still new in flutter so can anyone let me know if there is already a widget for this kind of switch or how should I make one ??
Also, I want the data shown below this button to change if I choose the other button but I guess that's obvious.
Thanks in advance.
You can use the TabBar widget to achieve this. I added a full example demonstrating how you can create this using the TabBar widget:
CODE
class StackOver extends StatefulWidget {
#override
_StackOverState createState() => _StackOverState();
}
class _StackOverState extends State<StackOver>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 2, vsync: this);
super.initState();
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Tab bar',
),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
// give the tab bar a height [can change hheight to preferred height]
Container(
height: 45,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(
25.0,
),
),
child: TabBar(
controller: _tabController,
// give the indicator a decoration (color and border radius)
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(
25.0,
),
color: Colors.green,
),
labelColor: Colors.white,
unselectedLabelColor: Colors.black,
tabs: [
// first tab [you can add an icon using the icon property]
Tab(
text: 'Place Bid',
),
// second tab [you can add an icon using the icon property]
Tab(
text: 'Buy Now',
),
],
),
),
// tab bar view here
Expanded(
child: TabBarView(
controller: _tabController,
children: [
// first tab bar view widget
Center(
child: Text(
'Place Bid',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
),
// second tab bar view widget
Center(
child: Text(
'Buy Now',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
],
),
),
);
}
}
OUTPUT
Try out this you have to change some colour and font:-
import 'package:flutter/material.dart';
typedef SwitchOnChange = Function(int);
class CustomSwitch extends StatefulWidget {
SwitchOnChange onChange;
CustomSwitch({this.onChange});
#override
State<StatefulWidget> createState() {
return CustomSwitchState();
}
}
class CustomSwitchState extends State<CustomSwitch>
with TickerProviderStateMixin {
AnimationController controller;
Animation animation;
GlobalKey key = GlobalKey();
#override
void initState() {
Future.delayed(Duration(milliseconds: 100)).then((v) {
controller = AnimationController(
vsync: this, duration: Duration(milliseconds: 300));
tabWidth = key.currentContext.size.width / 2;
// var width = (media.size.width - (2 * media.padding.left)) / 2;
animation = Tween<double>(begin: 0, end: tabWidth).animate(controller);
setState(() {});
controller.addListener(() {
setState(() {});
});
});
super.initState();
}
var selectedValue = 0;
double tabWidth = 0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
selectedValue == 0 ? this.controller.forward() : controller.reverse();
selectedValue = selectedValue == 0 ? 1 : 0;
},
child: Container(
key: key,
height: 44,
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(22)),
child: Stack(
children: <Widget>[
Row(
children: <Widget>[
Transform.translate(
offset: Offset(animation?.value ?? 0, 0),
child: Container(
height: 44,
width: tabWidth,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22),
boxShadow: [
BoxShadow(color: Colors.grey, blurRadius: 3),
]),
),
),
],
),
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: tabWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.directions_walk),
SizedBox(width: 5),
Text("Place Bid")
],
),
),
Container(
width: tabWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.directions_walk),
SizedBox(width: 5),
Text("Buy now")
],
),
)
],
),
),
],
),
),
);
}
}
The following is my workaround, which I believe to be the best method.
import 'package:flutter/material.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({
super.key,
});
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Text('Settings'),
bottom: PreferredSize(
preferredSize: Size.fromHeight(AppBar().preferredSize.height),
child: Container(
height: 50,
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 5,
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
10,
),
color: Colors.grey[200],
),
child: TabBar(
labelColor: Colors.white,
unselectedLabelColor: Colors.black,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(
10,
),
color: Colors.pink,
),
tabs: const [
Tab(
text: 'Basic',
),
Tab(
text: 'Advanced',
)
],
),
),
),
),
),
body: const TabBarView(
children: [
Center(
child: Text(
'Basic Settings',
style: TextStyle(
fontSize: 30,
),
),
),
Center(
child: Text(
'Advanced Settings',
style: TextStyle(
fontSize: 30,
),
),
),
],
),
),
);
}
}
You can use also PageView widget.
const double borderRadius = 25.0;
class CustomSwitchState extends StatefulWidget {
#override
_CustomSwitchStateState createState() => _CustomSwitchStateState();
}
class _CustomSwitchStateState extends State<CustomSwitchState> with SingleTickerProviderStateMixin {
PageController _pageController;
int activePageIndex = 0;
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: _menuBar(context),
),
Expanded(
flex: 2,
child: PageView(
controller: _pageController,
physics: const ClampingScrollPhysics(),
onPageChanged: (int i) {
FocusScope.of(context).requestFocus(FocusNode());
setState(() {
activePageIndex = i;
});
},
children: <Widget>[
ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Center(child: Text("Place Bid"),),
),
ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: Center(child: Text("Buy Now"),),
),
],
),
),
],
),
),
),
));
}
Widget _menuBar(BuildContext context) {
return Container(
width: 300.0,
height: 50.0,
decoration: const BoxDecoration(
color: Color(0XFFE0E0E0),
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
onTap: _onPlaceBidButtonPress,
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.center,
decoration: (activePageIndex == 0) ? const BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
) : null,
child: Text(
"Place Bid",
style: (activePageIndex == 0) ? TextStyle(color: Colors.white) : TextStyle(color: Colors.black),
),
),
),
),
Expanded(
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
onTap: _onBuyNowButtonPress,
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.center,
decoration: (activePageIndex == 1) ? const BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
) : null,
child: Text(
"Buy Now",
style: (activePageIndex == 1) ? TextStyle(color: Colors.white, fontWeight: FontWeight.bold) : TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
),
),
),
],
),
);
}
void _onPlaceBidButtonPress() {
_pageController.animateToPage(0,
duration: const Duration(milliseconds: 500), curve: Curves.decelerate);
}
void _onBuyNowButtonPress() {
_pageController.animateToPage(1,
duration: const Duration(milliseconds: 500), curve: Curves.decelerate);
}
}
OUTPUT
If you want tab layout like this you can use this
Output:
import 'package:flutter/material.dart';
import 'package:icons_helper/icons_helper.dart';
class DetailScreen extends StatefulWidget {
var body;
String title = "";
DetailScreen(this.body, this.title);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<DetailScreen> with TickerProviderStateMixin {
late int _startingTabCount;
List<Tab> _tabs = <Tab>[];
List<Widget> _generalWidgets = <Widget>[];
late TabController _tabController;
#override
void initState() {
_startingTabCount = widget.body["related_modules"].length;
_tabs = getTabs(_startingTabCount);
_tabController = getTabController();
super.initState();
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
bottom: TabBar(
isScrollable: true,
tabs: _tabs,
controller: _tabController,
),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.grey,
Colors.blue,
],
stops: [0.3, 1.0],
),
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
onPressed: () {
Navigator.of(context, rootNavigator: true).pop(context);
},
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.skip_previous),
color: Colors.white,
onPressed: () {
goToPreviousPage();
},
),
Container(
margin: EdgeInsets.only(right: 15),
child: IconButton(
icon: Icon(Icons.skip_next),
color: Colors.white,
onPressed: () {
goToNextPage();
},
),
)
],
),
body: Column(
children: <Widget>[
Expanded(
child: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: _tabController,
children: getWidgets(),
),
),
],
),
);
}
TabController getTabController() {
return TabController(length: _tabs.length, vsync: this)
..addListener(_updatePage);
}
Tab getTab(int widgetNumber) {
return Tab(
icon: Column(
children: [
if (widget.body["related_modules"][widgetNumber]["icon"].toString() ==
"fa-comments-o") ...[
Icon(
Icons.comment_outlined,
),
] else if (widget.body["related_modules"][widgetNumber]["icon"]
.toString() ==
"fa-map-marker") ...[
Icon(
Icons.location_on_rounded,
),
] else if (widget.body["related_modules"][widgetNumber]["icon"]
.toString() ==
"fa-address-card") ...[
Icon(
Icons.contact_page_sharp,
),
] else ...[
Icon(
getIconUsingPrefix(
name: widget.body["related_modules"][widgetNumber]["icon"]
.toString()
.substring(3),
),
)
]
],
),
text: widget.body["related_modules"][widgetNumber]["label"].toString(),
);
}
Widget getWidget(int widgetNumber) {
return Center(
child: Text("Widget nr: $widgetNumber"),
);
}
List<Tab> getTabs(int count) {
_tabs.clear();
for (int i = 0; i < count; i++) {
_tabs.add(getTab(i));
}
return _tabs;
}
List<Widget> getWidgets() {
_generalWidgets.clear();
for (int i = 0; i < _tabs.length; i++) {
_generalWidgets.add(getWidget(i));
}
return _generalWidgets;
}
void _updatePage() {
setState(() {});
}
//Tab helpers
bool isFirstPage() {
return _tabController.index == 0;
}
bool isLastPage() {
return _tabController.index == _tabController.length - 1;
}
void goToPreviousPage() {
_tabController.animateTo(_tabController.index - 1);
}
void goToNextPage() {
isLastPage()
? showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("End reached"),
content: Text("This is the last page.")))
: _tabController.animateTo(_tabController.index + 1);
}
}