Using persistent_bottom_nav_bar: ^5.0.2, how do I detect that the user is scrolling on a list views of items on connection_view.dart page from bottom_nav_bar.dart page? So if it detects, I want the bottom_nav_bar to be hidden when user scrolls down.
bottom_nav_bar.dart, my bottom navigation bar
Widget build(BuildContext context) {
return Sizer(builder: ((context, orientation, deviceType) {
return Scaffold(
body: PersistentTabView(context,
screens: const [
ClientConnection(),
ClientDiscover(),
ClientDiscover(),
ClientDiscover()
],
items: _navBarsItems(),
navBarStyle: NavBarStyle.style1,
bottomScreenMargin: 0,
navBarHeight: 60,
padding: const NavBarPadding.all(5),
margin: const EdgeInsets.only(
bottom: 15,
left: 20,
right: 20,
),
decoration: NavBarDecoration(
borderRadius: BorderRadius.circular(30),
boxShadow: const [
BoxShadow(
color: Colors.grey,
blurRadius: 5,
offset: Offset(2, 2))
])),
List<PersistentBottomNavBarItem> _navBarsItems() {
return [
PersistentBottomNavBarItem(
icon: SvgPicture.asset(
'assets/active-connection-icon.svg',
width: 5.w,
),
title: ("CONNECTIONS"),
inactiveIcon: SvgPicture.asset(
'assets/inactive-connection-icon.svg',
width: 5.w,
),
),
PersistentBottomNavBarItem(
icon: SvgPicture.asset(
'assets/active-discover-icon.svg',
width: 5.w,
),
title: ("DISCOVER"),
inactiveIcon: SvgPicture.asset(
'assets/inactive-discover-icon.svg',
width: 5.w,
),
),
PersistentBottomNavBarItem(
icon: SvgPicture.asset(
'assets/active-profile-icon.svg',
width: 5.w,
),
title: ("PROFILE"),
inactiveIcon: SvgPicture.asset(
'assets/inactive-profile-icon.svg',
width: 5.w,
),
),
PersistentBottomNavBarItem(
icon: SvgPicture.asset(
'assets/active-notification-icon.svg',
width: 5.w,
),
title: ("NOTIFICATIONS"),
inactiveIcon: SvgPicture.asset(
'assets/inactive-notification-icon.svg',
width: 5.w,
),
),
connection_view.dart,my list view of items
Widget build(BuildContext context) {
return Sizer(builder: ((context, orientation, deviceType) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
toolbarHeight: 10.h,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Connections',
style: TextStyle(color: Colors.black),
),
SizedBox(
width: 2.w,
),
Container(
width: 10.w,
height: 6.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.blue,
),
),
],
),
),
body: ListView.builder(
controller: _scrollController,
physics: ClampingScrollPhysics(),
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
itemCount: 20,
),
);
}));
You need two things: listen to scrolling events and update the visibility of your app bar accordingly.
To do that you will need a StatefulWidget which contains the app bar as well, create two members in the state class to track scrolling direction and app bar visibility:
bool _showAppBar = true;
bool _isScrollingDown = false;
Wrap the app bar into a Visibility widget depending on the _showAppBar member:
...
Visibility(visible: _showAppBar, child: YourBottomNavBarWidgetHere()),
...
Register the listener to detect scrolling direction and update the visibility. I suggest using initState and dispose methods:
#override
void initState() {
super.initState();
_scrollController.addListener(_scrollListener);
}
#override
void dispose() {
_scrollController.removeListener(_scrollListener);
super.dispose();
}
scrollListener() {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.reverse && !_isScrollingDown) {
_isScrollingDown = true;
setState(() {
_showAppBar = false;
});
}
else if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward && _isScrollingDown) {
_isScrollingDown = false;
setState(() {
_showAppBar = true;
});
}
}
Alternatively, you can use an AnimatedContainer instead of Visibility. This way the app bar will be hidden and shown animated:
AnimatedContainer(
height: _showAppBar ? kToolbarHeight : 0.0,
duration: const Duration(milliseconds: 300),
child: YourBottomNavBarWidgetHere()),
Related
What I have done till now is make widget draggable and a drag target according to my requirements. What i want is when the user drops the draggable container into the drag target i want to show that dragged widget inside that big drag target container and able to score if that is the right option or not.
second problem i am having is that i am using Text to speech for the sound and it is not working i tried many things but still it isn't working. i have a play button and give it a letter like A,B,D,G etc. whenever user presses the button i want to play the sound of that button and drag the right container into the drag target. Thankyou for your help.
// ignore_for_file: prefer_const_constructors, file_names, non_constant_identifier_names
import 'package:dyslexia/pages/round_2/Q8sound.dart';
import 'package:dyslexia/utilities/nextButton.dart';
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
import '../../utilities/QuestionWidget.dart';
class Q1DAD extends StatefulWidget {
const Q1DAD({Key? key}) : super(key: key);
#override
State<Q1DAD> createState() => _Q1DADState();
}
class _Q1DADState extends State<Q1DAD> {
String question =
"Listen to this letter sound and then choose the letter whose sound you hear";
var changeButton = false;
var score = 0;
final FlutterTts flutterTts = FlutterTts();
speak(word) async {
await flutterTts.setLanguage("en-US");
await flutterTts.setPitch(1);
await flutterTts.setVolume(10.0);
await flutterTts.speak(word);
}
var word = "M";
bool isPlayingMsg = false;
bool draged = false;
bool showDRag = false;
#override
Widget build(BuildContext context) {
bool isPlayingMsg = false;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
backgroundColor: Colors.cyan,
title: Text("AD&DY"),
),
body: Padding(
padding: const EdgeInsets.all(14.0),
child: Column(children: [
QuestionWidget(question: question),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
Material(
elevation: 5,
color: Colors.cyan[50],
child: ListTile(
onTap: () async {
speak(word);
isPlayingMsg = true;
},
leading: Icon(isPlayingMsg ? Icons.stop : Icons.play_arrow),
title: Text(isPlayingMsg ? "Stop" : "Play",
textScaleFactor: 1.2,
style: TextStyle(
color: Colors.black,
)),
),
),
Divider(
thickness: 1.0,
),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
Column(
children: [
DragTarget1(),
SizedBox(height: MediaQuery.of(context).size.height * 0.07),
Wrap(
spacing: 10,
runSpacing: 5,
children: [
draggableWord("M", false, score),
draggableWord("N", false, score),
draggableWord("O", false, score),
draggableWord("R", false, score),
],
),
],
),
SizedBox(height: MediaQuery.of(context).size.height * 0.1),
nextButton(changeButton: changeButton, Location: Q8sound()),
]),
),
);
}
int acceptedData = 0;
Widget DragTarget1() {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Material(
elevation: 10,
borderRadius: BorderRadius.circular(80),
child: Container(
height: 100.0,
width: 250.0,
color: Color.fromARGB(255, 78, 166, 178),
child: draged
? Container(
height: 50,
width: 70,
color: Colors.lightGreenAccent,
child: Align(
child: Text(word),
),
)
: Center(
child: Text('Drag target here'),
),
),
);
},
onAccept: (int data) {
setState(() {
draged = true;
showDRag = true;
});
},
),
],
);
}
Widget draggableWord(word, option, score) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
showDRag
? SizedBox()
: Draggable<int>(
// Data is the value this Draggable stores.
data: 10,
feedback: Container(
color: Colors.yellow[100],
height: 50,
width: 70,
child: Center(child: Text(word)),
),
childWhenDragging: Material(
elevation: 5,
borderRadius: BorderRadius.circular(10),
child: Container(
height: 50.0,
width: 70.0,
color: Colors.grey,
child: Center(
child: Text(""),
),
),
),
child: Material(
elevation: 5,
borderRadius: BorderRadius.circular(10),
child: Container(
height: 50,
width: 70,
color: Colors.lightGreenAccent,
child: Center(
child: Text(word),
),
),
),
)
],
);
}
}
here is my question widget
import 'package:flutter/material.dart';
class QuestionWidget extends StatelessWidget {
const QuestionWidget({
Key? key,
required this.question,
}) : super(key: key);
final String question;
#override
Widget build(BuildContext context) {
return Material(
elevation: 12,
color: Color.fromARGB(255, 125, 200, 210),
shadowColor: Colors.grey,
borderRadius: BorderRadius.circular(8),
child: Container(
height: 80,
width: 280,
alignment: Alignment.center,
child: Text(question,
textScaleFactor: 1.2,
style: TextStyle(
color: Colors.black,
))),
);
}
}
I am developing an audio player application using flutter, I m using on_audio_query package to fetch audio files from storage, and just_audio package for the audio player.
I want to know how to create something like the bar that is shown in this image
thanks in advance
I wrote one solution in a dartpad for you: https://dartpad.dev/?id=491a65532b2f92590c71a48be4836135
As in my example, you can use a stream to update the progress indicator around the play button. Look at my getSecondsFromCurrentMinute method. Replace this with the stream from your package.
Full code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: Colors.black,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: Colors.black,
body: Align(
alignment: Alignment.bottomCenter,
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
// Get the the seconds from current minute.
//
// TODO: Make this your actual progress indicator
Stream<int> getSecondsFromCurrentMinute() async* {
final now = DateTime.now();
final seconds = now.second;
yield seconds;
await Future.delayed(Duration(seconds: 1 - seconds));
yield* getSecondsFromCurrentMinute();
}
#override
Widget build(BuildContext context) {
return FractionallySizedBox(
heightFactor: .15,
widthFactor: 1,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Song cover
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(10)),
),
// Padding
SizedBox(width: 15),
// Title and artist
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title
Text(
"AUD-20190208-WA0007",
style: Theme.of(context).textTheme.headline5,
),
// Artist
Text(
"Unknown artist",
style: Theme.of(context)
.textTheme
.bodyText2
?.copyWith(color: Colors.grey.withOpacity(.6)),
),
],
),
// Padding between first 2 columns and Icons
Expanded(child: SizedBox.expand()),
//
// Play button and progress indicator
//
StreamBuilder<int>(
stream: getSecondsFromCurrentMinute(),
builder: (context, AsyncSnapshot<int> snapshot) {
double percentageOfSecond = (snapshot.data ?? 0) / 60;
return Container(
width: 40,
height: 40,
child: Stack(
children: [
// the circle showing progress
Positioned(
top: 0,
left: 0,
child: Container(
width: 40,
height: 40,
child: CircularProgressIndicator(
value: percentageOfSecond,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.red,
),
backgroundColor: Colors.red.withOpacity(0.15),
),
),
),
// the play arrow, inside the circle
Positioned(
top: 0,
left: 0,
child: Container(
width: 35,
height: 35,
child: IconButton(
icon: Icon(
Icons.play_arrow,
color: Colors.red,
),
onPressed: () {},
),
),
),
],
),
);
}),
SizedBox(width: 8),
Container(
width: 40,
height: 40,
child: GestureDetector(
onTap: () {},
child: Icon(
Icons.skip_next,
color: Colors.red,
),
),
),
//
SizedBox(width: 8),
Container(
width: 40,
height: 40,
child: GestureDetector(
onTap: () {},
child: Icon(
Icons.menu,
color: Colors.red,
size: 35,
),
),
),
// Extra padding at the end of the row
SizedBox(width: 30),
],
),
);
}
}
You can use Slider widget to make progress bar.
#override
Widget build(BuildContext context) {
return Slider(
value: position.inSeconds.toDouble(),
min: 0.0,
max: duration.inSeconds.toDouble() ,
onChanged: (value) async {
final position = Duration(seconds: value.toInt());
await player.seek(position);
},
),
And put the duration and position value in the initState()
Duration duration = Duration.zero;
Duration position = Duration.zero;
#override
void initState() {
player.currentPosition.listen((positionValue){
setState(() {
position = positionValue;
});
});
player.current.listen((event) {
setState(() {
duration = event.audio.duration;
});
});
So I have created a chat app which draws from a pusher client. Whenever there is a new message, the build function does rebuild, and I believe the widget list does change, but there is no update on the screen. How do I fix this ?
Widget build(BuildContext context) {
// print(messageWidgetList.length);
return Scaffold(
backgroundColor: AppColors.lightGrey,
appBar: AppBar(
backgroundColor: Colors.transparent,
title: Text(
messageTo,
style: TextStyle(
color: AppColors.white,
fontSize: 22,
),
),
),
body: Stack(
children: [
Padding(
padding:
const EdgeInsets.only(top: 12, left: 12, right: 12, bottom: 70),
child: ValueListenableBuilder<List<Widget>>(
valueListenable: messageWidgetList,
builder: (context, value, widget) {
print("Updated");
print(value.length);
// print(widget);
return ListView.builder(
// controller: scrollController,
physics: AlwaysScrollableScrollPhysics(),
reverse: true,
addAutomaticKeepAlives: true,
itemCount: value.length,
itemBuilder: (ctx, index) {
// print(index);
return value[index];
},
);
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (xFilesImages.isNotEmpty)
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: xFilesImages.map<Widget>((element) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: SizedBox(
height: 100,
width: 80,
child: Image.file(
File(element.path),
frameBuilder:
(ctx, child, frame, wasSynchronouslyLoaded) {
return SizedBox(
width: MediaQuery.of(ctx).size.width,
height: MediaQuery.of(ctx).size.height,
child: Stack(
children: [
Align(
alignment: Alignment.topRight,
child: Container(
height: 25,
width: 25,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.lightestGrey,
),
child: FittedBox(
child: GestureDetector(
onTap: () {
xFilesImages.remove(element);
setState(() {});
},
child:
const Icon(Icons.cancel)),
),
),
),
child
],
),
);
},
),
),
);
}).toList(),
),
),
const SizedBox(height: 5),
Container(
height: 60,
width: MediaQuery.of(context).size.width,
child: Padding(
padding:
const EdgeInsets.only(left: 10, bottom: 10, right: 10),
child: Container(
// height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColors.darkGrey,
),
child: TextFormField(
// expands: true,
style: TextStyle(color: AppColors.white),
focusNode: messageFocusNode,
controller: messageController,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(
right: 8, left: 8, top: 14),
prefixIcon: InkWell(
onTap: () async {
if (!(await Permission.camera.isGranted)) {
await Permission.camera.request();
await Permission.photos.request();
}
ImagePicker _picker = ImagePicker();
xFilesImages =
await _picker.pickMultiImage() ?? [];
print("Got xFiles");
print(xFilesImages.length);
for (XFile xFile in xFilesImages) {
print(xFile.name);
print(xFile.path);
}
setState(() {});
},
child: Icon(
Icons.attachment,
size: 34,
color: AppColors.lightestGrey,
),
),
suffixIcon: GestureDetector(
onTap: () async {
//TODO: When you wake up, you have implemented picking images. Work on displaying picked images and then sending them
// loading = true;
// messageController.text = '';
if (messageController.text.isNotEmpty ||
xFilesImages.isNotEmpty) {
messageFocusNode.unfocus();
// messageWidgetList.add(sentMessage(
// {"message": messageController.text}));
setState(() {});
print("Sent button clicked");
ApiProvider.sendMessage(
widget.userModel.bearerToken,
widget.senderPhone.phoneNumbers.first,
messageTo,
messageController.text,
xFilesImages);
// loading = false;
messageController.text = '';
xFilesImages = [];
setState(() {});
}
},
child: const Icon(
Icons.send,
size: 30,
color: const Color(0xFF004b77),
),
),
fillColor: AppColors.lightGrey,
hintText: "Enter message...",
hintStyle:
TextStyle(color: AppColors.lightestGrey)),
),
),
),
),
],
),
),
if (loading)
Container(
height: double.infinity,
width: double.infinity,
color: AppColors.lightGrey.withOpacity(0.3),
child: Center(
child: SpinKitChasingDots(
color: AppColors.blue,
)),
)
],
),
);
}
Bad, does not work
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: items); // <-- look here
}
Good, does update properly
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: [...items]); // <-- look here
}
Grandious with the little extra mile
static final List<Widget> items= [];
Widget build(BuildContext context) {
return ListView(children: <Widget>[...items]); // <-- look here
}
setState needs a brand new object to update properly. It does not look into a List like here if something changed in there.
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);
}
}
I am creating a set of selections using ChoiceChip widget. I wanted to make the chips to have transparent background under certain condition like this image
I tried putting backgroundColor: Colors.transparent, but it'll turn white instead of transparent.
Here is my codes:
String _selectedSize = "";
var sizes = ['XS', 'S', 'M', 'L', 'XL'];
_customChip(size) => InkWell(
child: Container(
width: 40.0,
height: 40.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.white,
),
child: Stack(
children: <Widget>[
Center(child: Text(size, style: _chipTextStyle,)),
Center(
child: RotationTransition(
turns: AlwaysStoppedAnimation(315/360),
child: Container(height: 1.0, color: Colors.grey),
),
),
],
),
),
);
return Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: sizes.map((size) {
return ChoiceChip(
pressElevation: 1.0,
backgroundColor: Colors.transparent, // this doesn't work
label: _customChip(size),
labelPadding: EdgeInsets.symmetric(horizontal: 2.0),
padding: EdgeInsets.all(2.0),
materialTapTargetSize: MaterialTapTargetSize.padded,
shape: CircleBorder(),
selected: _selectedSize == size,
selectedColor: _themeColor,
onSelected: (isSelected) {
setState(() {
_selectedSize = size;
});
},
);
}).toList(),
);
Is there any idea how to make it transparent, or I should use widgets other than ChoiceChip? Thanks!
The Chip widget has a material which is colored according to the Theme. You can change that by changing the Theme.canvasColor, like this:
Theme(
data: ThemeData(canvasColor: Colors.transparent),
child: Chip(
label:Container(/*your widget*/),
backgroundColor: Colors.transparent, // or any other color
),
)
Or, you can keep your old Theme (except the canvasColor) by doing this:
Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
child: Chip(
label: Container(/*your widget*/),
backgroundColor: Colors.transparent, // or any other color
),
)
I have tried so many thigns with ChoiceChips for transparent background and not getting success then i decided to do it in another way as you also asked for alternate option, so i have created example for you where it similarly works same as ChoiceChips:
Note: For unselected background color i used
"Colors.grey.withOpacity(0.1)" but you can also use
"Colors.transparent"
import 'package:flutter/material.dart';
class MyChoiceChipsRadio extends StatefulWidget {
createState() {
return CustomRadioState();
}
}
class CustomRadioState extends State<MyChoiceChipsRadio> {
List<RadioModel> sampleData = List<RadioModel>();
#override
void initState() {
// TODO: implement initState
super.initState();
sampleData.add(RadioModel(false, 'XS'));
sampleData.add(RadioModel(false, 'S'));
sampleData.add(RadioModel(false, 'M'));
sampleData.add(RadioModel(false, 'L'));
sampleData.add(RadioModel(false, 'XL'));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListItem"),
),
body: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/back_image.png"),
fit: BoxFit.cover,
),
)
),
ListView.builder(
itemCount: sampleData.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
//highlightColor: Colors.red,
splashColor: Colors.blueAccent,
onTap: () {
setState(() {
sampleData.forEach((element) => element.isSelected = false);
sampleData[index].isSelected = true;
});
},
child: RadioItem(sampleData[index]),
);
},
),
],
),
);
}
}
class RadioItem extends StatelessWidget {
final RadioModel _item;
RadioItem(this._item);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(15.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 50.0,
width: 50.0,
child: Center(
child: Text(_item.buttonText,
style: TextStyle(
color:
_item.isSelected ? Colors.red : Colors.grey,
//fontWeight: FontWeight.bold,
fontSize: 18.0)),
),
decoration: BoxDecoration(
color: _item.isSelected
? Colors.white
: Colors.grey.withOpacity(0.1),
shape: BoxShape.circle,
border: Border.all(color: _item.isSelected
? Colors.red
: Colors.grey, width: 1.0)
),
),
],
),
);
}
}
class RadioModel {
bool isSelected;
final String buttonText;
RadioModel(this.isSelected, this.buttonText);
}
Hope it helps :)