Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ink splashes get displayed over search widget #290

Closed
hlvs-apps opened this issue Jun 1, 2024 · 2 comments · Fixed by #291
Closed

Ink splashes get displayed over search widget #290

hlvs-apps opened this issue Jun 1, 2024 · 2 comments · Fixed by #291

Comments

@hlvs-apps
Copy link
Contributor

If the search widget is set, the ink splashes of half visible top rows get displayed over the search widget. The expected behaviour would be to hide them behind the search widget, because the search widget itself is not the source of the splash and the row that causes the splash itself is hidden behind the search widget.

ink_splash

Code to reproduce this issue:

import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
//import 'package:flutterfontchooser/flutterfontchooser.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: DropdownButtonHideUnderline(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Text("Width: ..."),
              Text("Height: 500px"),
              Text("..."),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Text("Bold"),
                  Text("Italic"),
                ],
              ),
              FontChooserDropdownMenuWithoutServerAccessAndNoFonts(),
              //FontChooserDropdownMenu(),
              //SizedBox(height: 200),
            ],
          ),
        ),
      ),
    );
  }
}

//a dropdown widget that allows the user to choose a font, and displays the font name in the dropdown.
//the fonts get dynamically loaded from the server
class FontChooserDropdownMenuWithoutServerAccessAndNoFonts
    extends StatefulWidget {
  //const FontChooserDropdownMenu({super.key, super.controller});
  const FontChooserDropdownMenuWithoutServerAccessAndNoFonts({super.key});

  @override
  State<FontChooserDropdownMenuWithoutServerAccessAndNoFonts> createState() =>
      _FontChooserDropdownMenuWithoutServerAccessAndNoFontsState();
}

class _FontChooserDropdownMenuWithoutServerAccessAndNoFontsState
    extends State<FontChooserDropdownMenuWithoutServerAccessAndNoFonts> {
  //final Map<SearchResponseEntryComparable, DropdownItem<SearchResponseEntryComparable>>
  final Map<String, DropdownItem<String>> _items = {};

  void _loadItems() {
    /*Set<SearchResponseEntryComparable> oldItems = _items.keys.toSet();
    bool changed = false;
    for (var value in controller.allFontsIfLoaded) {
      SearchResponseEntryComparable comparable =
      SearchResponseEntryComparable(value);
      if (!oldItems.contains(comparable)) {
        changed = true;
        _items[comparable] = DropdownItem<SearchResponseEntryComparable>(
          value: comparable,
          child: DropdownRowWidget(
            key: Key("${value.family}-dropdown-row-widget"),
            selectedFont: selectedFont,
            thisFont: value,
          ),
        );
      }
      oldItems.remove(comparable);
    }
    for (var item in oldItems) {
      _items.remove(item);
    }
    if (changed && mounted) {
        setState(() {});
    }*/
    for (int i = 0; i < 1500; i++) {
      _items["Font $i"] = DropdownItem<String>(
        value: "Font $i",
        child: DropdownRowWidget(
          key: Key("Font $i-dropdown-row-widget"),
          selectedFont: selectedFont,
          thisFont: "Font $i",
        ),
      );
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    ServicesBinding.instance.keyboard.addHandler(_onKey);
    Future.microtask(_loadItems);
  }

  /*@override
  void onController() {
    var s = searchResponseEntryComparableFromSearchResponseEntry(
        controller.selectedFont?.searchResponseEntry);
    if (s != selectedFont.value) {
      selectedFont.value = s;
    }
    _loadItems();
  }*/

  @override
  void dispose() {
    ServicesBinding.instance.keyboard.removeHandler(_onKey);
    super.dispose();
  }

  //final ValueNotifier<SearchResponseEntryComparable?> selectedFont =
  final ValueNotifier<String?> selectedFont = ValueNotifier(null);

  TextEditingController searchController = TextEditingController();

  FocusNode searchFocusNode = FocusNode();

  bool _onKey(KeyEvent event) {
    /*setState(() {
      //set random height
      maxHeight = 200 + (100 * (DateTime.now().second % 5));
    });*/
    if (_menuOpen && !searchFocusNode.hasFocus && event is KeyDownEvent) {
      String? key = event.character;
      if (key != null &&
          event.logicalKey != LogicalKeyboardKey.enter &&
          event.logicalKey != LogicalKeyboardKey.space &&
          event.logicalKey != LogicalKeyboardKey.tab) {
        if (event.logicalKey == LogicalKeyboardKey.backspace) {
          if (searchController.text.isNotEmpty) {
            searchController.text = "";
            searchFocusNode.requestFocus();
            return true;
          }
          searchFocusNode.requestFocus();
          return false;
        }
        searchController.text += key;
        searchFocusNode.requestFocus();
        return true;
      }
    }
    return false;
  }

  bool _menuOpen = false;

  @override
  Widget build(BuildContext context) {
    //return DropdownButton2<SearchResponseEntryComparable>(
    return Expanded(child:Column(
      children: [
        DropdownButton2<String>(
          valueListenable: selectedFont,
          barrierCoversButton: false,
          dropdownStyleData: DropdownStyleData(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(15),
            ),
          ),
          onMenuStateChange: (open) {
            _menuOpen = open;
          },
          buttonStyleData: const ButtonStyleData(
            width: 260,
            height: 50,
          ),
          dropdownSearchData: DropdownSearchData(
            searchController: searchController,
            searchBarWidgetHeight: 50,
            searchBarWidget: Container(
              height: 50,
              padding: const EdgeInsets.only(
                top: 8,
                bottom: 4,
                right: 8,
                left: 8,
              ),
              child: TextFormField(
                maxLines: 1,
                controller: searchController,
                focusNode: searchFocusNode,
                decoration: InputDecoration(
                  isDense: true,
                  contentPadding: const EdgeInsets.symmetric(
                    horizontal: 10,
                    vertical: 8,
                  ),
                  hintText: 'Search for a font...',
                  hintStyle: const TextStyle(fontSize: 12),
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                ),
              ),
            ),
            noResultsWidget: const Padding(
              padding: EdgeInsets.all(8),
              child: Text('No Font Found!'),
            ),
            searchMatchFn:
                //(DropdownItem<SearchResponseEntryComparable> entry, String search) {
                (DropdownItem<String> entry, String search) {
              //return entry.value?.family
              return entry.value
                      ?.toLowerCase()
                      .contains(search.toLowerCase()) ??
                  false;
            },
          ),
          iconStyleData: const IconStyleData(
            icon: Icon(
              Icons.arrow_drop_down,
              color: Colors.black45,
            ),
          ),
          menuItemStyleData: const MenuItemStyleData(
            padding: EdgeInsets.symmetric(horizontal: 16),
          ),
          hint: const Text(
            'Select a font',
            style: TextStyle(fontSize: 14),
          ),
          /*onChanged: (SearchResponseEntryComparable? newValue) {
        selectedFont.value = newValue;
        if (newValue == null) {
          controller.selectedFont = null;
          return;
        }
        Future.microtask(() async {
          controller.selectedFont =
          await LoadedFont.fromSearchResponseEntry(newValue.entry);
        });
      },*/
          onChanged: (String? newValue) {
            selectedFont.value = newValue;
          },
          items: _items.values.toList(),
        ),
      ],
    ),);
  }
}

class DropdownRowWidget extends StatefulWidget {
  //final SearchResponseEntry thisFont;
  final String thisFont;

  //final ValueNotifier<SearchResponseEntryComparable?> selectedFont;
  final ValueNotifier<String?> selectedFont;
  final double width;

  const DropdownRowWidget(
      {super.key,
      required this.thisFont,
      this.width = 200,
      required this.selectedFont});

  @override
  State<DropdownRowWidget> createState() => _DropdownRowWidgetState();
}

class _DropdownRowWidgetState extends State<DropdownRowWidget> {
  bool _isFontLoaded = false;
  bool _isLoading = false;

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        SizedBox(
          width: widget.width,
          child: Text(
            widget.thisFont,
            /*style: Theme.of(context)
                  .textTheme
                  .apply(fontFamily: widget.thisFont.menuFontFamily)
                  .bodyMedium*/
          ),
        ),
        if (!_isFontLoaded && !_isLoading)
          const Icon(
            Icons.cloud_download_outlined,
            color: Colors.grey,
            size: 24,
          ),
        /*if (!_isFontLoaded && _isLoading)
          LoadingAnimationWidget.dotsTriangle(
            color: Colors.grey,
            size: 24,
          ),*/
      ],
    );
  }

  void _check() {
    if (!mounted) {
      return;
    }
    bool s = false;
    //bool i = isFontLoaded(widget.thisFont);
    bool i = false;
    if (i != _isFontLoaded) {
      _isFontLoaded = i;
      s = true;
    }
    //i = isLoadingFont(widget.thisFont);
    i = false;
    if (i != _isLoading) {
      _isLoading = i;
      s = true;
    }
    if (s) {
      setState(() {});
    }
  }

  @override
  void initState() {
    super.initState();
    _check();
    //FontLoaderNotifier.instance.addListener(_check);
    widget.selectedFont.addListener(_check);
  }

  @override
  void didUpdateWidget(DropdownRowWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    _check();
  }

  @override
  void dispose() {
    //FontLoaderNotifier.instance.removeListener(_check);
    super.dispose();
  }
}
@hlvs-apps
Copy link
Contributor Author

Solved in PR #291

@AhmedLSayed9
Copy link
Owner

This is an issue in flutter which is being tracked in flutter/flutter#86584 & flutter/flutter#73315
but, we can accept the temporary solution in the PR for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants