Transparent sticky footer in Flutter - dart

I was following this Stack Overflow post for making a sticky footer. The footer performs like it needs to, but is there a way to add transparency so the items in the list behind the footer can be visible?
Solid footer
Transparent footer
Here is all the code for making the solid footer
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Column(
children: <Widget>[
new Expanded(
child: new ListView.builder(
itemCount: 200,
itemBuilder: (context, index) {
return new ListTile(
title: new Text("title $index"),
);
},
),
),
new Container(
height: 40.0,
color: Colors.red,
),
],
),
);
}
}

You can use Stack to display widgets on the top of each others.
new Scaffold(
body: new Stack(
alignment: Alignment.bottomCenter,
children: [
new ListView(...),
new Container(height: 40.0, color: Colors.red),
],
),
),

Related

How to fade-in a new screen from the bottom following the direction of a vertical drag action?

I need to navigate to a new screen with a fade-in animation from the bottom of the screen, following the direction of vertical drag on a custom material widget.
I have created two screens, Screen -1 and screen -2. On screen - 1, I have a Container widget. I have wrapped the widget inside a GestureDetector and I am trying Navigate to screen - 2 on vertical drag, which I am using GestureDetector.onVerticalDrag property.
import 'package:flutter/material.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/':(context) => ScreenOne(),
'/two': (context) => ScreenTwo(),
},
title: 'Screens',
);
}
}
class ScreenOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-1'),
),
body: GestureDetector(
onVerticalDragStart: (DragStartDetails details){
Navigator.pushNamed(context, '/two');
},
child: Container(
margin: EdgeInsets.all(8.0),
color: Colors.red,
),
),
);
}
}
class ScreenTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-2'),
),
body: Center(
child: Text('Screen-2'),
),
);
}
}
Can you please help me get the expected transition. I am attaching a GIF for reference.
If you don't mind using PageView, this is much easier with it and Opacity widget. Here is the demo video.
import 'package:flutter/material.dart';
class TestView extends StatefulWidget {
#override
_TestViewState createState() => _TestViewState();
}
class _TestViewState extends State<TestView> {
PageController pageController;
double panPosition = 1; // dummy value prevents division with 0
double deviceHeight;
void updatePageState() {
setState(() {
panPosition =
pageController.position.pixels.abs(); // updates pan position
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
pageController = PageController(
keepPage: true,
);
pageController
.addListener(updatePageState); // add listener to page controller.
}
#override
Widget build(BuildContext context) {
deviceHeight =
MediaQuery.of(context).size.height; //get device screen height
return Scaffold(
backgroundColor: Colors.white,
body: PageView(
controller: pageController,
scrollDirection: Axis.vertical, //vertical scroll
children: <Widget>[
Opacity(
// opacity handles the transition effect
opacity: 1 - (panPosition / deviceHeight),
//first screen opacity goes from 1 to 0
child: ScreenOne(),
),
Opacity(
opacity: (panPosition / deviceHeight),
//first screen opacity goes from 0 to 1
child: ScreenTwo(
title: "this title is from parent widget.",
),
)
],
),
);
}
}
class ScreenOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen-1'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 96,
color: Colors.red,
)
],
),
);
}
}
class ScreenTwo extends StatelessWidget {
final String title;
const ScreenTwo({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Text(title),
),
);
}
}

OverflowBox,the overflowed part cannot respond to the button?

I need to build a bottomNavigationBar with a vertical overflow TabBar item,
so I tried to use OverflowBox,it looks like useful.
but there is another problew,the overflowed part cannot respond to the button.
so what should I do make the GestureDetector effective?
or you have other ways to build a bottomNavigationBar like this?
thank you very much!
this is screen capture
this is main.dart:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'OverflowBox touch test'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
List<Color> _colors = [
Colors.blue,
Colors.green,
Colors.yellow,
];
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{
String _tip = "";
TabController controller;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new TabBarView(
controller: controller,
children: <Widget>[
new Center(child: new Text("page 1")),
new Center(child: new Text("page 2")),
new Center(child: new Text("page 3")),
],
),
bottomNavigationBar: new Container(
height: 72.0,
alignment: Alignment.bottomCenter,
color: const Color.fromRGBO(45, 45, 45, 1.0),
child: new TabBar(
controller: controller,
indicatorWeight: 0.01,
tabs: <Widget>[
_getBarItem(0),
_getBarItem(1),
_getBarItem(2),
],
),
),
);
}
#override
void initState() {
super.initState();
controller = new TabController(length: 3, vsync: this);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
Widget _getBarItem(int idx){
Widget ret = new Container(
width: 80.0,
height: idx==1?120.0:50.0,
color: _colors[idx],
);
if(idx==1){
ret = new OverflowBox(
maxHeight: double.infinity,
alignment: Alignment.bottomCenter,
child: new Container(
color: Colors.black,
child: new GestureDetector(
onTapDown: (x){
controller.animateTo(idx);
},
child: ret,
),
),
);
}
return ret;
}
}
Try to make your GestureDetector wrap your OverflowBox
Use this behavior on the Gesture Detector and then wrap your overflow with it.
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap(){..
)
You should use a Stack with a Positioned widget instead of an OverflowBox.
PS: Using functions that return widget should be avoided.
I can tell you the reason. OverflowBox widget layout is sizedByParent, so the size is 72. When hit test will ingore the pointer out the size. so the child widget overflowed part cannot respond to the button.

How to Conditionally Route Using One Button? (Flutter)

I have a scene (collections.dart) that takes an index of several other scenes/files in a PageView.builder. You can swipe between scenes from the collections.dart file. Also in collections.dart is a button.
I want it to be the case that if you click on the button, and the current scene being shown through collections.dart is, for example, FirstScreen, then I can route to a table I have built specifically for first.dart, with the same being true for all other scenes in the index.
I have tried to accomplish this by a conditional statement in the onPressed argument, but no success yet. There is no error, it just takes no action. Here is the code in its entirety for collections.dart (including the unsuccessful conditional statement for onPressed):
import 'package:flutter/material.dart';
import 'package:circle_indicator/circle_indicator.dart';
import 'first.dart';
import 'second.dart';
import 'third.dart';
import 'fourth.dart';
import 'fifth.dart';
import 'sixth.dart';
import 'seventh.dart';
import 'eighth.dart';
import 'ninth.dart';
import 'tenth.dart';
class CollectionsScreen extends StatelessWidget {
#override
Widget build(BuildContext context){
return Collections();
}
}
class Collections extends StatefulWidget {
#override
CollectionsState createState() => CollectionsState();
}
class CollectionsState extends State<Collections> {
FirstScreen one;
SecondScreen two;
ThirdScreen three;
FourthScreen four;
FifthScreen five;
SixthScreen six;
SeventhScreen seven;
EighthScreen eight;
NinthScreen nine;
TenthScreen ten;
List<Widget> pages;
#override
void initState() {
one = FirstScreen();
two = SecondScreen();
three = ThirdScreen();
four = FourthScreen();
five = FifthScreen();
six = SixthScreen();
seven = SeventhScreen();
eight = EighthScreen();
nine = NinthScreen();
ten = TenthScreen();
pages = [one, two, three, four, five, six, seven, eight, nine, ten];
super.initState();
}
final PageController controller = new PageController();
#override
Widget build(BuildContext context){
return new Stack(
children: <Widget>[
new Scaffold(
body: new Container(
child: new PageView.builder( //Swipe Between Pages
controller: controller,
itemCount: 10,
itemBuilder: (context, index){
return pages[index];
}
),
),
),
new Container( //CircleIndicator
child: new CircleIndicator(controller, 10, 8.0, Colors.white70, Colors.white,),
alignment: Alignment(0.0, 0.9),
),
new Container( //Button
alignment: Alignment(0.0, 0.65),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Container(
child: new RaisedButton(
elevation: 4.0,
child: new Text(
'SHOW ME',
style: new TextStyle(
fontWeight: FontWeight.w900,
fontSize: 22.0,
),
),
color: Color(0xFF70E0EF),
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(7.5)
),
//This is the conditional statement I'm talking about
onPressed: () {
new PageView.builder(
controller: controller,
itemBuilder: (context, index) {
if (pages[index] == one){
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new FirstTable()),
);
}
else if (pages[index] == two){
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new SecondTable()),
);
}
else {
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new ThirdTable()),
);
}
}
);
},
),
width: 150.0,
height: 60.0,
),
],
),
),
],
);
}
}
The "Table" classes I'm referring to in the conditional statement are in the files for first.dart, second.dart, etc. Here is the file for first.dart. For the moment, the code is identical between all these files (first.dart, second.dart, etc.):
import 'package:flutter/material.dart';
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new First();
}
}
class First extends StatefulWidget {
#override
FirstState createState() => FirstState();
}
class FirstState extends State<First>{
#override
Widget build(BuildContext context) {
double fontSize = MediaQuery.of(context).size.height;
double fontSizeFractional = fontSize * 0.07;
return Scaffold(
body: new Stack(
fit: StackFit.passthrough,
children: [
new Container( //Background
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/FirstBG.png'),
fit: BoxFit.cover
),
),
),
new Container( //Title
margin: EdgeInsets.all(40.0),
alignment: new Alignment(0.0, -0.70),
child: new Text(
'FIRST',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: fontSizeFractional,
color: Colors.white,
fontFamily: 'baron neue',
),
),
),
],
),
);
}
}
class FirstTable extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Table();
}
}
class Table extends StatefulWidget {
#override
TableState createState() => TableState();
}
class TableState extends State<Table>{
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: new RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: new Text(
'Go Back',
),
),
),
);
}
}
I have a theory that the reason it isn't working is that collections.dart isn't actually caching any data as to what page of the index it is on (that could be totally wrong, though). Curious to hear your ideas!
Your problem is that you should directly use controller.page inside the onPressed of your button. instead of instantiating a widget.
Although ultimately you should hide an abstract layer between your gallery class and the list of items.
To do that you can create a custom class which will hols all informations about a gallery item :
#immutable
class GalleryItem {
final Widget content;
final Widget details;
GalleryItem({#required this.content, this.details}) : assert(content != null);
}
Your gallery will then take a list of such class as parameter. And do it's job with these.
Ideally you want to use your gallery like this :
Gallery(
items: [
GalleryItem(
content: Container(
color: Colors.red,
),
details: Text("red"),
),
GalleryItem(
content: Container(
color: Colors.blue,
),
details: Text("blue"),
),
],
),
The code of such gallery would be :
class Gallery extends StatefulWidget {
final List<GalleryItem> items;
Gallery({#required this.items, Key key})
: assert(items != null),
super(key: key);
#override
_GalleryState createState() => _GalleryState();
}
class _GalleryState extends State<Gallery> {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: PageView(
children: widget.items.map((item) => item.content).toList(),
controller: pageController,
),
),
RaisedButton(
onPressed: showContentDetails,
child: Text("More info"),
)
],
);
}
void showContentDetails() {
final index = pageController.page.round();
if (widget.items[index]?.details != null) {
showDialog(
context: context,
builder: (_) =>
GalleryItemDetails(details: widget.items[index].details),
);
}
}
}
class GalleryItemDetails extends StatelessWidget {
final Widget details;
GalleryItemDetails({#required this.details, Key key})
: assert(details != null),
super(key: key);
#override
Widget build(BuildContext context) {
return Dialog(
child: details,
);
}
}

Flutter: Changing the current tab in tab bar view using a button

I am creating an app that contains a tab bar on its homepage. I want to be able to navigate to one of the tabs using my FloatingActionButton. In addition, I want to keep the default methods of navigating to that tab, i.e. by swiping on screen or by clicking the tab.
I also want to know how to link that tab to some other button.
Here is a screenshot of my homepage.
You need to get the TabBar controller and call its animateTo() method from the button onPressed() handle.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyTabbedPage(),
);
}
}
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => new _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {
final List<Tab> myTabs = <Tab>[
new Tab(text: 'LEFT'),
new Tab(text: 'RIGHT'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(vsync: this, length: myTabs.length);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Tab demo"),
bottom: new TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: new TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
return new Center(child: new Text(tab.text));
}).toList(),
),
floatingActionButton: new FloatingActionButton(
onPressed: () => _tabController.animateTo((_tabController.index + 1) % 2), // Switch tabs
child: new Icon(Icons.swap_horiz),
),
);
}
}
If you use a GlobalKey for the MyTabbedPageState you can get the controller from any place, so you can call the animateTo() from any button.
class MyApp extends StatelessWidget {
static final _myTabbedPageKey = new GlobalKey<_MyTabbedPageState>();
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyTabbedPage(
key: _myTabbedPageKey,
),
);
}
}
You could call it from anywhere doing:
MyApp._myTabbedPageKey.currentState._tabController.animateTo(...);
I am super late, but hopefully someone benefits from this. just add this line to your onPressed of your button and make sure to change the index number to your preferred index:
DefaultTabController.of(context).animateTo(1);
You can use TabController:
TabController _controller = TabController(
vsync: this,
length: 3,
initialIndex: 0,
);
_controller.animateTo(_currentTabIndex);
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _controller,
tabs: [
...
],
),
),
body: TabBarView(
controller: _controller,
children: [
...
],
),
);
And than, setState to update screen:
int _currentTabIndex = 0;
setState(() {
_currentTabIndex = 1;
});
chemamolin's answer above is correct, but for additional clarification/tip, if you want to call your tabcontroller "from anywhere", also make sure the tabcontroller is not a private property of the class by removing the underscore, otherwise the distant class will not be able to see the tabcontroller with the example provided even when using the GlobalKey.
In other words, change
TabController _tabController;
to:
TabController tabController;
and change
MyApp._myTabbedPageKey.currentState._tabController.animateTo(...);
to:
MyApp._myTabbedPageKey.currentState.tabController.animateTo(...);
and everywhere else you reference tabcontroller.
If you want to jump to a specific page, you can use
PageController.jumpToPage(int)
However if you need animation, you'd use
PageController.animateToPage(page, duration: duration, curve: curve)
Simple example demonstrating it.
// create a PageController
final _controller = PageController();
bool _shouldAnimate = true; // whether we animate or jump
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_shouldAnimate) {
// animates to page1 with animation
_controller.animateToPage(1, duration: Duration(seconds: 1), curve: Curves.easeOut);
} else {
// jump to page1 without animation
_controller.jumpToPage(1);
}
},
),
body: PageView(
controller: _controller, // assign it to PageView
children: <Widget>[
FlutterLogo(colors: Colors.orange), // page0
FlutterLogo(colors: Colors.green), // page1
FlutterLogo(colors: Colors.red), // page2
],
),
);
}
DefaultTabController(
length: 4,
initialIndex: 0,
child: TabBar(
tabs: [
Tab(
child: Text(
"People",
style: TextStyle(
color: Colors.black,
),
),
),
Tab(
child: Text(
"Events",
style: TextStyle(
color: Colors.black,
),
),
),
Tab(
child: Text(
"Places",
style: TextStyle(
color: Colors.black,
),
),
),
Tab(
child: Text(
"HashTags",
style: TextStyle(
color: Colors.black,
),
),
),
],
),
)
i was trying to solve similar issue but passing methods or controllers down the widget tree wasn't a clean option for me. i had requirement to go back to tabbed page from other non-tabbed routes (back to specific tabs).
following solution worked for me
Inside tabbed page: read route arguments
#override
Widget build(BuildContext context) {
final String? tabId = Get.arguments;
_selectedTabIndex = tabId !=null? int.parse(tabId): 0;
return Scaffold(
....
body: _pages[_selectedPageIndex]['page'] as Widget,
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
....);
}
Now the calling page
onSubmit:() { // or some other event
// do something here
Get.offAndToNamed(Routes.homeTabs,
arguments: TabIndex.specialTab.index.toString());
//Routes is a const & TabIndex is enum defined somewhere
}
A solution with TabController + Streams
Pass a stream into the state object. Pass the new tab index through the stream for the state to update itself. Here's how I'm doing it.
import 'package:flutter/material.dart';
class TabsWidget extends StatefulWidget {
const TabsWidget({Key? key, this.tabs = const [], this.changeReceiver}) : super(key: key);
final List<Tab> tabs;
// To change the tab from outside, pass in the tab index through a stream
final Stream<int>? changeReceiver;
#override
State<TabsWidget> createState() => _TabsWidgetState();
}
class _TabsWidgetState extends State<TabsWidget> with SingleTickerProviderStateMixin {
int _index = 0;
late TabController _tabController;
#override
void initState() {
_tabController = TabController(length: widget.tabs.length, vsync: this, initialIndex: _index);
// Listen to tab index changes from external sources via this stream
widget.changeReceiver?.listen((int newIndex) {
setState(() {
_index = newIndex;
_tabController.animateTo(newIndex);
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
if (widget.tabs.isEmpty) return const SizedBox.shrink(); // If no tabs, show nothing
return TabBar(tabs: widget.tabs, controller: _tabController, );
}
}
// Sample usage - main
import 'dart:async';
import 'package:flutter/material.dart';
import 'tabs_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final StreamController<int> tabChangeNotifier = StreamController();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tab Change Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Tab Change Demo'),
),
body: SingleChildScrollView(child: Column(
children: [
const SizedBox(height: 30,),
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
ElevatedButton(onPressed: () => tabChangeNotifier.add(0), child: const Text('Go Orange')),
ElevatedButton(onPressed: () => tabChangeNotifier.add(1), child: const Text('Go Red')),
ElevatedButton(onPressed: () => tabChangeNotifier.add(2), child: const Text('Go Green')),
],),
const SizedBox(height: 30,),
TabsWidget(changeReceiver: tabChangeNotifier.stream, tabs: const [
Tab(icon: Icon(Icons.circle, color: Colors.orange,),),
Tab(icon: Icon(Icons.circle, color: Colors.red,),),
Tab(icon: Icon(Icons.circle, color: Colors.green,),),
],),
],
),), // This trailing comma makes auto-formatting nicer for build methods.
),
);
}
#override
void dispose() {
tabChangeNotifier.close();
super.dispose();
}
}
This is how the above sample looks.
Use DefaultTabController instead of a local TabController, high enough in your widget tree, and then you'll have access to it from anywhere in that sub tree.
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: initialIndex,
length: tabs.length,
child: SizedBox( // From here down you have access to the tab controller
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SomeWidget(), // Has access to the controller
TabBar(
controller: DefaultTabController.of(context),
tabs:
tabs.map((tab) => Tab(child: Text(tab.title, style: const TextStyle(color: Colors.black)))).toList(),
),
Expanded(
child: TabBarView(
controller: DefaultTabController.of(context),
children: tabs.map((tab) => tab.widget).toList(),
),
),
],
),
),
);
}
In any point in that tree, you can access the tab controller with DefaultTabController.of(context) and change the tab, like so:
DefaultTabController.of(context)?.animateTo(0);
class Tab bar
class TabBarScreen extends StatefulWidget {
TabBarScreen({Key key}) : super(key: key);
#override
_TabBarScreenState createState() => _TabBarScreenState();
}
final List<Tab> tabs = <Tab>[
Tab(text: 'Page1'),
Tab(text: 'Page2'),
];
class _TabBarScreenState extends State<TabBarScreen> with SingleTickerProviderStateMixin {
TabController tabController;
#override
void initState() {
super.initState();
tabController = new TabController(vsync: this, length: tabs.length);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
centerTitle: true,
shape: Border(bottom: BorderSide(color: Colors.white)),
title: Text("Tab Bar",),
bottom: TabBar(
controller: tabController,
tabs: tabs,
indicatorWeight: 5,
indicatorColor: Colors.white,
labelColor: Colors.white,
),
),
body: TabBarView(
controller: tabController,
children: [
PageOneScreen(controller: tabController),
PageTwoScreen(controller: tabController),
],
),
),
);
}
}
class PageOne
class PageOneScreen extends StatefulWidget {
#override
_PageOneScreenState createState() => _PageOneScreenState();
PageOneScreen({controller}) {
tabController = controller;
}
}
TabController tabController;
class _PageOneScreenState extends State<PageOneScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
onPressed: () {
tabController.animateTo(1); // number : index page
},
child: Text(
"Go To Page 2",
),
),
],
);
}
}

How to highlight the border of a card selected?

I would like to highlight the border of this card whenever is selected, so the user will see that specific card has been selected.
Try this !
The Result :
The Code :
import 'package:flutter/material.dart';
void main() => runApp(
new MaterialApp(
home: new MyApp(),
),
);
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("NonstopIO"),
),
body: new ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return new MyCustomWidget(
title: "Title $index",
subtitle: "$index",
);
},
),
);
}
}
class MyCustomWidget extends StatefulWidget {
final String title;
final String subtitle;
const MyCustomWidget({Key key, this.title, this.subtitle}) : super(key: key);
#override
_MyCustomWidgetState createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget> {
bool selected = false;
#override
Widget build(BuildContext context) {
return new Card(
shape: selected
? new RoundedRectangleBorder(
side: new BorderSide(color: Colors.blue, width: 2.0),
borderRadius: BorderRadius.circular(4.0))
: new RoundedRectangleBorder(
side: new BorderSide(color: Colors.white, width: 2.0),
borderRadius: BorderRadius.circular(4.0)),
child: new Padding(
padding: const EdgeInsets.all(4.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(widget.title),
new Text(widget.subtitle),
new Checkbox(
value: selected,
onChanged: (value) {
setState(() {
selected = value;
});
})
],
),
),
);
}
}
I found something useful and similar to what I would like to achieve.
Flutter - I want to select the card by onLongPress?

Resources