1. 28 Feb, 2024 1 commit
  2. 27 Feb, 2024 2 commits
  3. 22 Feb, 2024 1 commit
  4. 20 Feb, 2024 1 commit
  5. 18 Jan, 2024 1 commit
  6. 02 Aug, 2023 1 commit
    • Taha Tesser's avatar
      Fix Scrollable `TabBar` for Material 3 (#131409) · 2c71881f
      Taha Tesser authored
      fixes [Material 3 `TabBar` does not take full width when `isScrollable: true`](https://github.com/flutter/flutter/issues/117722)
      
      ### Description
      1. Fixed the divider doesn't stretch to take all the available width in the scrollable tab bar in M3
      2. Added `dividerHeight` property.
      
      ### Code sample
      
      <details> 
      <summary>expand to view the code sample</summary> 
      
      ```dart
      import 'package:flutter/material.dart';
      
      /// Flutter code sample for [TabBar].
      
      void main() => runApp(const TabBarApp());
      
      class TabBarApp extends StatelessWidget {
        const TabBarApp({super.key});
      
        @override
        Widget build(BuildContext context) {
          return const MaterialApp(
            debugShowCheckedModeBanner: false,
            home: TabBarExample(),
          );
        }
      }
      
      class TabBarExample extends StatefulWidget {
        const TabBarExample({super.key});
      
        @override
        State<TabBarExample> createState() => _TabBarExampleState();
      }
      
      class _TabBarExampleState extends State<TabBarExample> {
        bool rtl = false;
        bool customColors = false;
        bool removeDivider = false;
        Color dividerColor = Colors.amber;
        Color indicatorColor = Colors.red;
      
        @override
        Widget build(BuildContext context) {
          return DefaultTabController(
            initialIndex: 1,
            length: 3,
            child: Directionality(
              textDirection: rtl ? TextDirection.rtl : TextDirection.ltr,
              child: Scaffold(
                appBar: AppBar(
                  title: const Text('TabBar Sample'),
                  actions: <Widget>[
                    IconButton.filledTonal(
                      tooltip: 'Switch direction',
                      icon: const Icon(Icons.swap_horiz),
                      onPressed: () {
                        setState(() {
                          rtl = !rtl;
                        });
                      },
                    ),
                    IconButton.filledTonal(
                      tooltip: 'Use custom colors',
                      icon: const Icon(Icons.color_lens),
                      onPressed: () {
                        setState(() {
                          customColors = !customColors;
                        });
                      },
                    ),
                    IconButton.filledTonal(
                      tooltip: 'Show/hide divider',
                      icon: const Icon(Icons.remove_rounded),
                      onPressed: () {
                        setState(() {
                          removeDivider = !removeDivider;
                        });
                      },
                    ),
                  ],
                ),
                body: Column(
                  children: <Widget>[
                    const Spacer(),
                    const Text('Scrollable - TabAlignment.start'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.start,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Text('Scrollable - TabAlignment.startOffset'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.startOffset,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Text('Scrollable - TabAlignment.center'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.center,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Spacer(),
                    const Text('Non-scrollable - TabAlignment.fill'),
                    TabBar(
                      tabAlignment: TabAlignment.fill,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Text('Non-scrollable - TabAlignment.center'),
                    TabBar(
                      tabAlignment: TabAlignment.center,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Spacer(),
                    const Text('Secondary - TabAlignment.fill'),
                    TabBar.secondary(
                      tabAlignment: TabAlignment.fill,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Text('Secondary - TabAlignment.center'),
                    TabBar.secondary(
                      tabAlignment: TabAlignment.center,
                      dividerColor: customColors ? dividerColor : null,
                      indicatorColor: customColors ? indicatorColor : null,
                      dividerHeight: removeDivider ? 0 : null,
                      tabs: const <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    const Spacer(),
                  ],
                ),
              ),
            ),
          );
        }
      }
      ``` 
      	
      </details>
      
      ### Before
      
      ![Screenshot 2023-07-27 at 14 12 36](https://github.com/flutter/flutter/assets/48603081/1c08a9d2-ac15-4d33-8fa1-c765b4b10f92)
      
      ### After 
      
      ![Screenshot 2023-07-27 at 14 13 12](https://github.com/flutter/flutter/assets/48603081/7e662dfe-9f32-46c9-a128-3024a4782882)
      
      This also contains regression test for https://github.com/flutter/flutter/pull/125974#discussion_r1239089151
      
      ```dart
        // This is a regression test for https://github.com/flutter/flutter/pull/125974#discussion_r1239089151.
        testWidgets('Divider can be constrained', (WidgetTester tester) async {
      ```
      
      ![Screenshot 2023-07-27 at 14 16 37](https://github.com/flutter/flutter/assets/48603081/ac2ef49b-2410-46d0-8ae2-d9b77236abba)
      2c71881f
  7. 22 Jun, 2023 2 commits
    • Kate Lovett's avatar
      Revert "Fix Material 3 Scrollable `TabBar`" (#129383) · 087377ea
      Kate Lovett authored
      Reverts flutter/flutter#125974
      087377ea
    • Taha Tesser's avatar
      Fix Material 3 Scrollable `TabBar` (#125974) · 32fde139
      Taha Tesser authored
      fix https://github.com/flutter/flutter/issues/117722
      
      ### Description
      1. Fix the divider doesn't stretch to take all the available width in the scrollable tab bar in M3
      2. Add `dividerHeight` property.
      3. Update the default tab alignment for the scrollable tab bar to match the specs (this is backward compatible for M2 with the new `tabAlignment` property).
      
      ### Bug (default tab alignment)
      
      ![Screenshot 2023-05-05 at 19 04 40](https://user-images.githubusercontent.com/48603081/236509483-1d03af21-a764-4776-acef-2126560f0d51.png)
      
      ### Fix (default tab alignment)
      
      ![Screenshot 2023-05-05 at 19 04 15](https://user-images.githubusercontent.com/48603081/236509513-2426d456-c54f-42bd-9545-a14dc6ee7e69.png)
      
      ### Code sample
      
      <details> 
      <summary>code sample</summary> 
      
      ```dart
      import 'package:flutter/material.dart';
      
      /// Flutter code sample for [TabBar].
      
      void main() => runApp(const TabBarApp());
      
      class TabBarApp extends StatelessWidget {
        const TabBarApp({super.key});
      
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            theme: ThemeData(
              //  tabBarTheme: const TabBarTheme(tabAlignment: TabAlignment.start),
                useMaterial3: true,
            ),
            home: const TabBarExample(),
          );
        }
      }
      
      class TabBarExample extends StatefulWidget {
        const TabBarExample({super.key});
      
        @override
        State<TabBarExample> createState() => _TabBarExampleState();
      }
      
      class _TabBarExampleState extends State<TabBarExample> {
        bool rtl = false;
      
        @override
        Widget build(BuildContext context) {
          return DefaultTabController(
            initialIndex: 1,
            length: 3,
            child: Directionality(
              textDirection:  rtl ? TextDirection.rtl : TextDirection.ltr,
              child: Scaffold(
                appBar: AppBar(
                  title: const Text('TabBar Sample'),
                ),
                body: const Column(
                  children: <Widget>[
                    Text('Scrollable-TabAlignment.start'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.start,
                      tabs: <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    Text('Scrollable-TabAlignment.startOffset'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.startOffset,
                      tabs: <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    Text('Scrollable-TabAlignment.center'),
                    TabBar(
                      isScrollable: true,
                      tabAlignment: TabAlignment.center,
                      tabs: <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    Spacer(),
                    Text('Non-scrollable-TabAlignment.fill'),
                    TabBar(
                      tabAlignment: TabAlignment.fill,
                      tabs: <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    Text('Non-scrollable-TabAlignment.center'),
                    TabBar(
                      tabAlignment: TabAlignment.center,
                      tabs: <Widget>[
                        Tab(
                          icon: Icon(Icons.cloud_outlined),
                        ),
                        Tab(
                          icon: Icon(Icons.beach_access_sharp),
                        ),
                        Tab(
                          icon: Icon(Icons.brightness_5_sharp),
                        ),
                      ],
                    ),
                    Spacer(),
                  ],
                ),
                floatingActionButton: FloatingActionButton.extended(
                  onPressed: () {
                    setState(() {
                      rtl = !rtl;
                    });
                  },
                  label: const Text('Switch Direction'),
                  icon: const Icon(Icons.swap_horiz),
                ),
              ),
            ),
          );
        }
      }
      ``` 
      	
      </details>
      
      ![Screenshot 2023-06-06 at 18 06 12](https://github.com/flutter/flutter/assets/48603081/5ee5386d-cc64-4025-a020-ed2222cb6031)
      32fde139
  8. 09 Jun, 2023 1 commit
    • Pierre-Louis's avatar
      Improve defaults generation with logging, stats, and token validation (#128244) · 66cda591
      Pierre-Louis authored
      ## Description
      
      This improves defaults generation with logging, stats, and token validation. 
      
      This PR includes these changes:
      * introduce `TokenLogger`, with a verbose mode
        * prints versions and tokens usage to the console
        * outputs `generated/used_tokens.csv`, a list of all used tokens, for use by Google
      * find token files in `data` automatically
      * hide tokens `Map`
        * tokens can be obtained using existing resolvers (e.g. `color`, `shape`), or directly through `getToken`.
        * tokens can be checked for existence with `tokenAvailable`
      * remove version from template, since the tokens are aggregated and multiple versions are possible (as is the case currently), it does not make sense to attribute a single version
      * improve documentation
      
      ## Related Issues
       - Fixes https://github.com/flutter/flutter/issues/122602
      
      ## Tests
       - Added tests for `TokenLogger`
       - Regenerated tokens, no-op except version removal
      
      ## Future work
      A future PR should replace or remove the following invalid tokens usages
      
      <img width="578" alt="image" src="https://github.com/flutter/flutter/assets/6655696/b6f9e5a7-523f-4f72-94f9-1b0bf4cc9f00">
      66cda591
  9. 11 May, 2023 1 commit
    • Qun Cheng's avatar
      Reorder `materialStateProperty` defaults (#125905) · 4e7e4512
      Qun Cheng authored
      Fixes #122250. This PR is to make sure all the MaterialStateProperty defaults are able to correctly resolve different states. 
      * When a widget is pressed, it is also hovered, so we need to put the `MaterialState.pressed` check before `MaterialState.hovered`. 
      * When a widget is focused, the widget should still be able to be hovered, so we should check `MaterialState.hovered` before `MaterialState.focused`.
      * There are also cases like in _InputDecoratorDefaultsM3, the `MaterialState.disabled` should be checked before `MaterialState.error`.
      
       the order should be disabled, (error), pressed, hovered, focused.
      4e7e4512
  10. 05 May, 2023 1 commit
  11. 01 May, 2023 1 commit
  12. 28 Mar, 2023 1 commit
  13. 27 Mar, 2023 1 commit
  14. 23 Mar, 2023 1 commit
  15. 30 Nov, 2022 2 commits
  16. 29 Nov, 2022 1 commit