Try with below way.
Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray
class and override the _prepareToRender method in order to add columns to dynamic fields block, change “add button” label etc.
<?php
namespace Namespace\Modulename\Block\Adminhtml\Form\Field;
use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
/**
* Class AdditionalEmail
*/
class AdditionalEmail extends AbstractFieldArray
{
/**
* {@inheritdoc}
*/
protected function _prepareToRender()
{
$this->addColumn('firstname', ['label' => __('First Name'), 'class' => 'required-entry']);
$this->addColumn('lastname', ['label' => __('Last Name')]);
$this->addColumn('email',['label' => __('Email'), 'size' => '50px', 'class' => 'required-entry validate-email']);
$this->_addAfter = false;
$this->_addButtonLabel = __('Add Email');
}
}
The data is saved in one record of the core_config_data table in the database for each system configuration field. That’s why we need to serialize the data before saving and unserialize this value back after loading.
<?php
namespace Namespace\Modulename\Config\Backend;
use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Value as ConfigValue;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;
use Magento\Framework\Serialize\SerializerInterface;
/**
* Class AdditionalEmail
*/
class AdditionalEmail extends ConfigValue
{
/**
* Json Serializer
*
* @var SerializerInterface
*/
protected $serializer;
/**
* ShippingMethods constructor
*
* @param SerializerInterface $serializer
* @param Context $context
* @param Registry $registry
* @param ScopeConfigInterface $config
* @param TypeListInterface $cacheTypeList
* @param AbstractResource|null $resource
* @param AbstractDb|null $resourceCollection
* @param array $data
*/
public function __construct(
SerializerInterface $serializer,
Context $context,
Registry $registry,
ScopeConfigInterface $config,
TypeListInterface $cacheTypeList,
AbstractResource $resource = null,
AbstractDb $resourceCollection = null,
array $data = []
) {
$this->serializer = $serializer;
parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
}
/**
* Prepare data before save
*
* @return void
*/
public function beforeSave()
{
/** @var array $value */
$value = $this->getValue();
unset($value['__empty']);
$encodedValue = $this->serializer->serialize($value);
$this->setValue($encodedValue);
}
/**
* Process data after load
*
* @return void
*/
protected function _afterLoad()
{
/** @var string $value */
$value = $this->getValue();
$decodedValue = $this->serializer->unserialize($value);
$this->setValue($decodedValue);
}
}
etc/adminhtml/system.xml
file
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="sample" translate="label" sortOrder="50">
<label>Sample</label>
</tab>
<section id="test_sample" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<class>separator-top</class>
<label>Test Sample</label>
<tab>sample</tab>
<resource>Namespace_Modulename::dynamic_fields</resource>
<group id="additional" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Additional</label>
<field id="emails" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Additional Emails</label>
<frontend_model>Namespace\Modulename\Block\Adminhtml\Form\Field\AdditionalEmail</frontend_model>
<backend_model>Namespace\Modulename\Config\Backend\AdditionalEmail</backend_model>
</field>
</group>
</section>
</system>
</config>
For more information click here
I hope it helps!