skip to Main Content

New Flutter coder here, so my existing knowledge of how various components interact is limited. I’ve read through/attempted various tutorials (including several posted here), but am having trouble implementing with my existing code to achieve the desired result.

My existing code displays the following: current view

What I’m trying to achieve is the following: desired view

As pictured, I’d like to create a custom indicator for the top-most TabBar. Any help would be greatly appreciated. Thank you!

HomePage code:

import 'package:fallout/plans.dart';
import 'package:fallout/recipes.dart';
import 'package:flutter/material.dart';
import 'package:flutter_glow/flutter_glow.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  _HomePageState createState() => _HomePageState();
}

const myColor = Color(0xFFF8EA00);

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 5,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.black,
          title: const Center(
            child: TabBar(
              indicatorColor: Colors.black,
              indicatorWeight: 2,
              dividerColor: myColor,
              isScrollable: true,
              tabs: <Widget>[
                Tab(
                  child: GlowText(
                    blurRadius: 3,
                    'HOME',
                    style: TextStyle(color: myColor, fontSize: 18.0),
                  ),
                ),
                Tab(
                  child: GlowText(
                    blurRadius: 3,
                    'PLANS',
                    style: TextStyle(color: myColor, fontSize: 18.0),
                  ),
                ),
                Tab(
                  child: GlowText(
                    blurRadius: 3,
                    'RECIPES',
                    style: TextStyle(color: myColor, fontSize: 18.0),
                  ),
                ),
                Tab(
                  child: GlowText(
                    blurRadius: 3,
                    'CREATURES',
                    style: TextStyle(color: myColor, fontSize: 18.0),
                  ),
                ),
                Tab(
                  child: GlowText(
                    blurRadius: 3,
                    'PLANTS',
                    style: TextStyle(color: myColor, fontSize: 18.0),
                  ),
                ),
              ],
            ),
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            const Text('Home'),
            PlansTabs(0xffff5722),
            RecipesTabs(0xffff5722),
            const Text('Creatures'),
            const Text('Plants'),
          ],
        ),
      ),
    );
  }
}

Plans Tab Code:

import 'package:flutter/material.dart';
import 'package:flutter_glow/flutter_glow.dart';

class PlansTabs extends StatefulWidget {
  PlansTabs(this.colorVal);
  int colorVal;

  _PlansTabsState createState() => _PlansTabsState();
}

class _PlansTabsState extends State<PlansTabs>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = new TabController(vsync: this, length: 4);
    _tabController.addListener(_handleTabSelection);
  }

  void _handleTabSelection() {
    setState(() {
      widget.colorVal = 0xffff5722;
    });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.black,
          toolbarHeight: double.minPositive,
          bottom: TabBar(
            controller: _tabController,
            isScrollable: true,
            indicatorColor: Color(0xFFF8EA00),
            indicatorWeight: 1,
            dividerColor: Colors.black,
            tabs: const <Widget>[
              Tab(
                child: GlowText(
                  blurRadius: 3,
                  'ARMOR',
                  style: TextStyle(color: Color(0xFFF8EA00), fontSize: 18.0),
                ),
              ),
              Tab(
                child: GlowText(
                  blurRadius: 3,
                  'C.A.M.P.',
                  style: TextStyle(color: Color(0xFFF8EA00), fontSize: 18.0),
                ),
              ),
              Tab(
                child: GlowText(
                  blurRadius: 3,
                  'MODS',
                  style: TextStyle(color: Color(0xFFF8EA00), fontSize: 18.0),
                ),
              ),
              Tab(
                child: GlowText(
                  blurRadius: 3,
                  'WEAPONS',
                  style: TextStyle(color: Color(0xFFF8EA00), fontSize: 18.0),
                ),
              ),
            ],
          ),
        ),
        body: TabBarView(
          controller: _tabController,
          children: <Widget>[
            //PlansTabs(0xffff5722),
            Container(1
              height: 200.0,
              child: Center(child: Text('Armor Text')),
            ),
            Container(
              height: 200.0,
              child: Center(child: Text('C.A.M.P. Text')),
            ),
            Container(
              height: 200.0,
              child: Center(child: Text('Mods Text')),
            ),
            Container(
              height: 200.0,
              child: Center(child: Text('Weapons Text')),
            ),
          ],
        ),
      ),
    );
  }
}

I’ve tried referencing the following solutions, but can’t seem to integrate it properly with my code:

2

Answers


  1. I think it’s not possible to achieve the desired style within Flutter built-in TabBar widget. You’ve to make your own Custom TabBar widget. I’ve tried many things before to style tabbar. Hope you’ll get a hint from this.

    class CustomTabBar extends StatelessWidget implements PreferredSizeWidget {
      final List<String> tabTitles;
      final TabController controller;
    
      const CustomTabBar(
          {super.key, required this.tabTitles, required this.controller});
    
      @override
      Size get preferredSize => const Size.fromHeight(52);
    
      @override
      Widget build(BuildContext context) {
        return SizedBox(
          height: preferredSize.height,
          child: TabBar(
            indicatorSize: TabBarIndicatorSize.label,
            indicatorPadding: const EdgeInsets.only(right: -24, left: -24),
            indicator: const BoxDecoration(
              border: Border(
                top: BorderSide(color: Color(0xFFF8EA00), width: 2),
                right: BorderSide(color: Color(0xFFF8EA00), width: 2),
                left: BorderSide(color: Color(0xFFF8EA00), width: 2),
                bottom: BorderSide(color: Colors.black, width: 4),
              ),
            ),
            // indicatorColor: Colors.black,
            controller: controller,
            tabs: [
              for (int i = 0; i < tabTitles.length; i++)
                Tab(
                  child: Text(
                    tabTitles[i],
                    maxLines: null,
                    overflow: TextOverflow.ellipsis,
                    textAlign: TextAlign.center,
                    style: const TextStyle(color: Colors.white),
                  ),
                ),
            ],
          ),
        );
      }
    }
    

    Outfup

    Login or Signup to reply.
  2. demo:

    enter image description here

    code:

    import 'package:flutter/material.dart';
    import 'package:flutter_glow/flutter_glow.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key});
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    const myColor = Color(0xFFF8EA00);
    
    class _HomePageState extends State<HomePage> {
      final tabs = [
        'HOME',
        'PLANS',
        'RECIPES',
        'CREATURES',
        'PLANTS',
      ];
    
      @override
      Widget build(BuildContext context) {
        return DefaultTabController(
          length: 5,
          child: Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.black,
              title: Center(
                child: Builder(
                  builder: (context) {
                    final ctrl = DefaultTabController.of(context);
                    return TabBar(
                      indicatorColor: Colors.transparent,
                      dividerColor: Colors.transparent,
                      isScrollable: true,
                      tabs: tabs.map((tabText) {
                        final tabWidget = Tab(
                          child: GlowText(
                            tabText,
                            blurRadius: 3,
                            style: const TextStyle(
                              color: Color(0xFFF8EA00),
                              fontSize: 18.0,
                            ),
                          ),
                        );
                        if (tabText == 'HOME') {
                          return CustomPaint(
                            painter: TabsPainter(controller: ctrl),
                            child: tabWidget,
                          );
                        }
                        return tabWidget;
                      }).toList(),
                    );
                  },
                ),
              ),
            ),
            body: const TabBarView(
              children: <Widget>[
                Text('Home'),
                Text('Plans'),
                Text('Recipes'),
                Text('Creatures'),
                Text('Plants'),
              ],
            ),
          ),
        );
      }
    }
    
    class TabsPainter extends CustomPainter {
      final TabController controller;
    
      TabsPainter({
        required this.controller,
      }) : super(repaint: controller);
    
      final List<double> widths = [89, 98, 111, 141, 108];
    
      @override
      void paint(Canvas canvas, Size size) {
        final animation = controller.animation!;
        final paint = Paint()..color = myColor;
        paint.style = PaintingStyle.stroke;
        paint.strokeWidth = 1.5;
        final dx = sumUntil(animation.value);
        final dx2 = sumUntil(animation.value + 1);
        final path = Path()..moveTo(-15, 47);
        path.relativeLineTo(dx, 0);
        path.relativeLineTo(0, -23);
        path.relativeLineTo(8, 0);
        path.moveTo(dx2 - 27, 24);
        path.relativeLineTo(8, 0);
        path.relativeLineTo(0, 23);
        path.lineTo(sumUntil(5) - 20, 47);
        canvas.drawPath(path, paint);
      }
    
      double sumUntil(double animation) {
        double distance = 0;
        final index = animation.floor();
        for (int i = 0; i < index; i++) {
          distance += widths[i];
        }
        if (index < widths.length) {
          final leftover = animation - index;
          distance += leftover * widths[index];
        }
        return distance;
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) => false;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search